From 6cb6eef615d757b18566709a1645140155c88f0e Mon Sep 17 00:00:00 2001 From: Michael Goderbauer Date: Fri, 23 Oct 2020 09:12:42 -0700 Subject: [PATCH] Make Material/CupertinoLocalizations non-nullable (#68807) --- .../lib/src/cupertino/bottom_tab_bar.dart | 11 +---- .../lib/src/cupertino/date_picker.dart | 6 +-- packages/flutter/lib/src/cupertino/debug.dart | 46 +++++++++++++++++++ .../flutter/lib/src/cupertino/dialog.dart | 2 +- .../lib/src/cupertino/localizations.dart | 12 +++-- packages/flutter/lib/src/cupertino/route.dart | 2 +- .../lib/src/cupertino/text_selection.dart | 10 ++-- packages/flutter/lib/src/material/about.dart | 12 ++--- .../flutter/lib/src/material/app_bar.dart | 4 +- .../flutter/lib/src/material/back_button.dart | 4 +- .../src/material/bottom_navigation_bar.dart | 2 +- .../lib/src/material/bottom_sheet.dart | 4 +- packages/flutter/lib/src/material/chip.dart | 2 +- packages/flutter/lib/src/material/dialog.dart | 7 ++- packages/flutter/lib/src/material/drawer.dart | 5 +- .../flutter/lib/src/material/dropdown.dart | 4 +- .../flutter/lib/src/material/expand_icon.dart | 2 +- .../lib/src/material/expansion_panel.dart | 2 +- .../src/material/material_localizations.dart | 13 ++++-- .../lib/src/material/navigation_rail.dart | 2 +- .../src/material/paginated_data_table.dart | 2 +- .../pickers/calendar_date_picker.dart | 8 ++-- .../pickers/calendar_date_range_picker.dart | 6 +-- .../pickers/date_picker_deprecated.dart | 2 +- .../material/pickers/date_picker_dialog.dart | 2 +- .../pickers/date_range_picker_dialog.dart | 8 ++-- .../material/pickers/input_date_picker.dart | 10 ++-- .../pickers/input_date_range_picker.dart | 12 ++--- .../flutter/lib/src/material/popup_menu.dart | 12 ++--- .../lib/src/material/refresh_indicator.dart | 2 +- .../lib/src/material/reorderable_list.dart | 2 +- packages/flutter/lib/src/material/search.dart | 2 +- .../flutter/lib/src/material/stepper.dart | 2 +- packages/flutter/lib/src/material/tabs.dart | 2 +- .../flutter/lib/src/material/text_field.dart | 2 +- .../lib/src/material/text_selection.dart | 2 +- packages/flutter/lib/src/material/theme.dart | 2 +- packages/flutter/lib/src/material/time.dart | 2 +- .../flutter/lib/src/material/time_picker.dart | 40 ++++++++-------- .../material/user_accounts_drawer_header.dart | 4 +- packages/flutter/test/cupertino/app_test.dart | 4 +- .../flutter/test/cupertino/debug_test.dart | 33 +++++++++++++ .../test/cupertino/localizations_test.dart | 24 ++++++++++ packages/flutter/test/material/app_test.dart | 4 +- .../test/material/localizations_test.dart | 24 ++++++++++ .../test/material/time_picker_test.dart | 2 +- 46 files changed, 245 insertions(+), 122 deletions(-) create mode 100644 packages/flutter/lib/src/cupertino/debug.dart create mode 100644 packages/flutter/test/cupertino/debug_test.dart diff --git a/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart b/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart index 3fc8bda3b3..862eb5b57e 100644 --- a/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart +++ b/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart @@ -209,14 +209,7 @@ class CupertinoTabBar extends StatelessWidget implements PreferredSizeWidget { List _buildTabItems(BuildContext context) { final List result = []; - final CupertinoLocalizations? localizations = CupertinoLocalizations.of(context); - assert( - localizations != null, - 'CupertinoTabBar requires a Localizations parent in order to provide an ' - 'appropriate Semantics hint for tab indexing. A CupertinoApp will ' - 'provide the DefaultCupertinoLocalizations, or you can instantiate your ' - 'own Localizations.' - ); + final CupertinoLocalizations localizations = CupertinoLocalizations.of(context); for (int index = 0; index < items.length; index += 1) { final bool active = index == currentIndex; @@ -226,7 +219,7 @@ class CupertinoTabBar extends StatelessWidget implements PreferredSizeWidget { Expanded( child: Semantics( selected: active, - hint: localizations!.tabSemanticsLabel( + hint: localizations.tabSemanticsLabel( tabIndex: index + 1, tabCount: items.length, ), diff --git a/packages/flutter/lib/src/cupertino/date_picker.dart b/packages/flutter/lib/src/cupertino/date_picker.dart index da422f3884..e7e40f581f 100644 --- a/packages/flutter/lib/src/cupertino/date_picker.dart +++ b/packages/flutter/lib/src/cupertino/date_picker.dart @@ -623,7 +623,7 @@ class _CupertinoDatePickerDateTimeState extends State { super.didChangeDependencies(); textDirectionFactor = Directionality.of(context) == TextDirection.ltr ? 1 : -1; - localizations = CupertinoLocalizations.of(context)!; + localizations = CupertinoLocalizations.of(context); alignCenterLeft = textDirectionFactor == 1 ? Alignment.centerLeft : Alignment.centerRight; alignCenterRight = textDirectionFactor == 1 ? Alignment.centerRight : Alignment.centerLeft; @@ -1113,7 +1113,7 @@ class _CupertinoDatePickerDateState extends State { super.didChangeDependencies(); textDirectionFactor = Directionality.of(context) == TextDirection.ltr ? 1 : -1; - localizations = CupertinoLocalizations.of(context)!; + localizations = CupertinoLocalizations.of(context); alignCenterLeft = textDirectionFactor == 1 ? Alignment.centerLeft : Alignment.centerRight; alignCenterRight = textDirectionFactor == 1 ? Alignment.centerRight : Alignment.centerLeft; @@ -1621,7 +1621,7 @@ class _CupertinoTimerPickerState extends State { super.didChangeDependencies(); textDirection = Directionality.of(context)!; - localizations = CupertinoLocalizations.of(context)!; + localizations = CupertinoLocalizations.of(context); _measureLabelMetrics(); } diff --git a/packages/flutter/lib/src/cupertino/debug.dart b/packages/flutter/lib/src/cupertino/debug.dart new file mode 100644 index 0000000000..6706ec72b7 --- /dev/null +++ b/packages/flutter/lib/src/cupertino/debug.dart @@ -0,0 +1,46 @@ +// 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/foundation.dart'; +import 'package:flutter/widgets.dart'; + +import 'localizations.dart'; + +/// Asserts that the given context has a [Localizations] ancestor that contains +/// a [CupertinoLocalizations] delegate. +/// +/// To call this function, use the following pattern, typically in the +/// relevant Widget's build method: +/// +/// ```dart +/// assert(debugCheckHasCupertinoLocalizations(context)); +/// ``` +/// +/// Does nothing if asserts are disabled. Always returns true. +bool debugCheckHasCupertinoLocalizations(BuildContext context) { + assert(() { + if (Localizations.of(context, CupertinoLocalizations) == null) { + throw FlutterError.fromParts([ + ErrorSummary('No CupertinoLocalizations found.'), + ErrorDescription( + '${context.widget.runtimeType} widgets require CupertinoLocalizations ' + 'to be provided by a Localizations widget ancestor.' + ), + ErrorDescription( + 'The cupertino library uses Localizations to generate messages, ' + 'labels, and abbreviations.' + ), + ErrorHint( + 'To introduce a CupertinoLocalizations, either use a ' + 'CupertinoApp at the root of your application to include them ' + 'automatically, or add a Localization widget with a ' + 'CupertinoLocalizations delegate.' + ), + ...context.describeMissingAncestor(expectedAncestorType: CupertinoLocalizations) + ]); + } + return true; + }()); + return true; +} diff --git a/packages/flutter/lib/src/cupertino/dialog.dart b/packages/flutter/lib/src/cupertino/dialog.dart index ba656b6073..cac46fe10a 100644 --- a/packages/flutter/lib/src/cupertino/dialog.dart +++ b/packages/flutter/lib/src/cupertino/dialog.dart @@ -226,7 +226,7 @@ class CupertinoAlertDialog extends StatelessWidget { @override Widget build(BuildContext context) { - final CupertinoLocalizations localizations = CupertinoLocalizations.of(context)!; + final CupertinoLocalizations localizations = CupertinoLocalizations.of(context); final bool isInAccessibilityMode = _isInAccessibilityMode(context); final double textScaleFactor = MediaQuery.of(context)!.textScaleFactor; return CupertinoUserInterfaceLevel( diff --git a/packages/flutter/lib/src/cupertino/localizations.dart b/packages/flutter/lib/src/cupertino/localizations.dart index 5d99a45e0a..b56c849a98 100644 --- a/packages/flutter/lib/src/cupertino/localizations.dart +++ b/packages/flutter/lib/src/cupertino/localizations.dart @@ -5,7 +5,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; -import 'date_picker.dart'; +import 'debug.dart'; /// Determines the order of the columns inside [CupertinoDatePicker] in /// time and date time mode. @@ -242,8 +242,11 @@ abstract class CupertinoLocalizations { /// The `CupertinoLocalizations` from the closest [Localizations] instance /// that encloses the given context. /// + /// If no [CupertinoLocalizations] are available in the given `context`, this + /// method throws an exception. + /// /// This method is just a convenient shorthand for: - /// `Localizations.of(context, CupertinoLocalizations)`. + /// `Localizations.of(context, CupertinoLocalizations)!`. /// /// References to the localized resources defined by this class are typically /// written in terms of this method. For example: @@ -251,8 +254,9 @@ abstract class CupertinoLocalizations { /// ```dart /// CupertinoLocalizations.of(context).anteMeridiemAbbreviation; /// ``` - static CupertinoLocalizations? of(BuildContext context) { - return Localizations.of(context, CupertinoLocalizations); + static CupertinoLocalizations of(BuildContext context) { + debugCheckHasCupertinoLocalizations(context); + return Localizations.of(context, CupertinoLocalizations)!; } } diff --git a/packages/flutter/lib/src/cupertino/route.dart b/packages/flutter/lib/src/cupertino/route.dart index d1ddf5d2d0..d32f7d4e66 100644 --- a/packages/flutter/lib/src/cupertino/route.dart +++ b/packages/flutter/lib/src/cupertino/route.dart @@ -1146,7 +1146,7 @@ Future showCupertinoDialog({ return showGeneralDialog( context: context, barrierDismissible: barrierDismissible, - barrierLabel: CupertinoLocalizations.of(context)!.modalBarrierDismissLabel, + barrierLabel: CupertinoLocalizations.of(context).modalBarrierDismissLabel, barrierColor: CupertinoDynamicColor.resolve(_kModalBarrierColor, context)!, // This transition duration was eyeballed comparing with iOS transitionDuration: const Duration(milliseconds: 250), diff --git a/packages/flutter/lib/src/cupertino/text_selection.dart b/packages/flutter/lib/src/cupertino/text_selection.dart index b2c86d2a06..8e93c90d1f 100644 --- a/packages/flutter/lib/src/cupertino/text_selection.dart +++ b/packages/flutter/lib/src/cupertino/text_selection.dart @@ -152,7 +152,7 @@ class _CupertinoTextSelectionToolbarWrapperState extends State<_CupertinoTextSel } final List items = []; - final CupertinoLocalizations? localizations = CupertinoLocalizations.of(context); + final CupertinoLocalizations localizations = CupertinoLocalizations.of(context); final EdgeInsets arrowPadding = widget.isArrowPointingDown ? EdgeInsets.only(bottom: _kToolbarArrowSize.height) : EdgeInsets.only(top: _kToolbarArrowSize.height); @@ -183,17 +183,17 @@ class _CupertinoTextSelectionToolbarWrapperState extends State<_CupertinoTextSel } if (widget.handleCut != null) { - addToolbarButton(localizations!.cutButtonLabel, widget.handleCut!); + addToolbarButton(localizations.cutButtonLabel, widget.handleCut!); } if (widget.handleCopy != null) { - addToolbarButton(localizations!.copyButtonLabel, widget.handleCopy!); + addToolbarButton(localizations.copyButtonLabel, widget.handleCopy!); } if (widget.handlePaste != null && _clipboardStatus.value == ClipboardStatus.pasteable) { - addToolbarButton(localizations!.pasteButtonLabel, widget.handlePaste!); + addToolbarButton(localizations.pasteButtonLabel, widget.handlePaste!); } if (widget.handleSelectAll != null) { - addToolbarButton(localizations!.selectAllButtonLabel, widget.handleSelectAll!); + addToolbarButton(localizations.selectAllButtonLabel, widget.handleSelectAll!); } return CupertinoTextSelectionToolbar._( diff --git a/packages/flutter/lib/src/material/about.dart b/packages/flutter/lib/src/material/about.dart index 648df10158..b09ccf20bc 100644 --- a/packages/flutter/lib/src/material/about.dart +++ b/packages/flutter/lib/src/material/about.dart @@ -201,7 +201,7 @@ class AboutListTile extends StatelessWidget { assert(debugCheckHasMaterialLocalizations(context)); return ListTile( leading: icon, - title: child ?? Text(MaterialLocalizations.of(context)!.aboutListTileTitle( + title: child ?? Text(MaterialLocalizations.of(context).aboutListTileTitle( applicationName ?? _defaultApplicationName(context), )), dense: dense, @@ -405,7 +405,7 @@ class AboutDialog extends StatelessWidget { ), actions: [ TextButton( - child: Text(MaterialLocalizations.of(context)!.viewLicensesButtonLabel), + child: Text(MaterialLocalizations.of(context).viewLicensesButtonLabel), onPressed: () { showLicensePage( context: context, @@ -417,7 +417,7 @@ class AboutDialog extends StatelessWidget { }, ), TextButton( - child: Text(MaterialLocalizations.of(context)!.closeButtonLabel), + child: Text(MaterialLocalizations.of(context).closeButtonLabel), onPressed: () { Navigator.pop(context); }, @@ -493,7 +493,7 @@ class _LicensePageState extends State { Widget build(BuildContext context) { return _MasterDetailFlow( detailPageFABlessGutterWidth: _getGutterSize(context), - title: Text(MaterialLocalizations.of(context)!.licensesPageTitle), + title: Text(MaterialLocalizations.of(context).licensesPageTitle), detailPageBuilder: _packageLicensePage, masterViewBuilder: _packagesView, ); @@ -720,7 +720,7 @@ class _PackageListTile extends StatelessWidget { color: isSelected ? Theme.of(context)!.highlightColor : Theme.of(context)!.cardColor, child: ListTile( title: Text(packageName), - subtitle: Text(MaterialLocalizations.of(context)!.licensesPackageDetailText(numberLicenses)), + subtitle: Text(MaterialLocalizations.of(context).licensesPackageDetailText(numberLicenses)), selected: isSelected, onTap: onTap, ), @@ -890,7 +890,7 @@ class _PackageLicensePageState extends State<_PackageLicensePage> { @override Widget build(BuildContext context) { assert(debugCheckHasMaterialLocalizations(context)); - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); final ThemeData? theme = Theme.of(context); final String title = widget.packageName; final String subtitle = localizations.licensesPackageDetailText(widget.licenseEntries.length); diff --git a/packages/flutter/lib/src/material/app_bar.dart b/packages/flutter/lib/src/material/app_bar.dart index fe09faf42e..5b63f28662 100644 --- a/packages/flutter/lib/src/material/app_bar.dart +++ b/packages/flutter/lib/src/material/app_bar.dart @@ -543,7 +543,7 @@ class _AppBarState extends State { leading = IconButton( icon: const Icon(Icons.menu), onPressed: _handleDrawerButton, - tooltip: MaterialLocalizations.of(context)!.openAppDrawerTooltip, + tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip, ); } else { if (!hasEndDrawer && canPop) @@ -616,7 +616,7 @@ class _AppBarState extends State { actions = IconButton( icon: const Icon(Icons.menu), onPressed: _handleDrawerButtonEnd, - tooltip: MaterialLocalizations.of(context)!.openAppDrawerTooltip, + tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip, ); } diff --git a/packages/flutter/lib/src/material/back_button.dart b/packages/flutter/lib/src/material/back_button.dart index 75cc24a94f..a193296170 100644 --- a/packages/flutter/lib/src/material/back_button.dart +++ b/packages/flutter/lib/src/material/back_button.dart @@ -98,7 +98,7 @@ class BackButton extends StatelessWidget { return IconButton( icon: const BackButtonIcon(), color: color, - tooltip: MaterialLocalizations.of(context)!.backButtonTooltip, + tooltip: MaterialLocalizations.of(context).backButtonTooltip, onPressed: () { if (onPressed != null) { onPressed!(); @@ -152,7 +152,7 @@ class CloseButton extends StatelessWidget { return IconButton( icon: const Icon(Icons.close), color: color, - tooltip: MaterialLocalizations.of(context)!.closeButtonTooltip, + tooltip: MaterialLocalizations.of(context).closeButtonTooltip, onPressed: () { if (onPressed != null) { onPressed!(); diff --git a/packages/flutter/lib/src/material/bottom_navigation_bar.dart b/packages/flutter/lib/src/material/bottom_navigation_bar.dart index 9c345432e7..c34c545423 100644 --- a/packages/flutter/lib/src/material/bottom_navigation_bar.dart +++ b/packages/flutter/lib/src/material/bottom_navigation_bar.dart @@ -823,7 +823,7 @@ class _BottomNavigationBarState extends State with TickerPr } List _createTiles() { - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); assert(localizations != null); final ThemeData themeData = Theme.of(context)!; diff --git a/packages/flutter/lib/src/material/bottom_sheet.dart b/packages/flutter/lib/src/material/bottom_sheet.dart index 0087473c25..349a0b4e91 100644 --- a/packages/flutter/lib/src/material/bottom_sheet.dart +++ b/packages/flutter/lib/src/material/bottom_sheet.dart @@ -370,7 +370,7 @@ class _ModalBottomSheetState extends State<_ModalBottomSheet> { assert(debugCheckHasMediaQuery(context)); assert(debugCheckHasMaterialLocalizations(context)); final MediaQueryData? mediaQuery = MediaQuery.of(context); - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); final String routeLabel = _getRouteLabel(localizations); return AnimatedBuilder( @@ -671,7 +671,7 @@ Future showModalBottomSheet({ builder: builder, theme: Theme.of(context, shadowThemeOnly: true), isScrollControlled: isScrollControlled, - barrierLabel: MaterialLocalizations.of(context)!.modalBarrierDismissLabel, + barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, backgroundColor: backgroundColor, elevation: elevation, shape: shape, diff --git a/packages/flutter/lib/src/material/chip.dart b/packages/flutter/lib/src/material/chip.dart index 21b7cc635f..22f1604548 100644 --- a/packages/flutter/lib/src/material/chip.dart +++ b/packages/flutter/lib/src/material/chip.dart @@ -1789,7 +1789,7 @@ class _RawChipState extends State with TickerProviderStateMixin showDialog({ return dialog; }, barrierDismissible: barrierDismissible, - barrierLabel: MaterialLocalizations.of(context)!.modalBarrierDismissLabel, + barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, barrierColor: barrierColor ?? Colors.black54, transitionDuration: const Duration(milliseconds: 150), transitionBuilder: _buildMaterialDialogTransitions, diff --git a/packages/flutter/lib/src/material/drawer.dart b/packages/flutter/lib/src/material/drawer.dart index ba6f9652ef..e12426b9ea 100644 --- a/packages/flutter/lib/src/material/drawer.dart +++ b/packages/flutter/lib/src/material/drawer.dart @@ -180,13 +180,12 @@ class Drawer extends StatelessWidget { switch (Theme.of(context)!.platform) { case TargetPlatform.iOS: case TargetPlatform.macOS: - label = semanticLabel; break; case TargetPlatform.android: case TargetPlatform.fuchsia: case TargetPlatform.linux: case TargetPlatform.windows: - label = semanticLabel ?? MaterialLocalizations.of(context)?.drawerLabel; + label = semanticLabel ?? MaterialLocalizations.of(context).drawerLabel; } return Semantics( scopesRoute: true, @@ -566,7 +565,7 @@ class DrawerControllerState extends State with SingleTickerPro child: GestureDetector( onTap: close, child: Semantics( - label: MaterialLocalizations.of(context)?.modalBarrierDismissLabel, + label: MaterialLocalizations.of(context).modalBarrierDismissLabel, child: MouseRegion( opaque: true, child: Container( // The drawer's "scrim" diff --git a/packages/flutter/lib/src/material/dropdown.dart b/packages/flutter/lib/src/material/dropdown.dart index 8fb994601b..34a37acf7d 100644 --- a/packages/flutter/lib/src/material/dropdown.dart +++ b/packages/flutter/lib/src/material/dropdown.dart @@ -253,7 +253,7 @@ class _DropdownMenuState extends State<_DropdownMenu> { // When the menu is dismissed we just fade the entire thing out // in the first 0.25s. assert(debugCheckHasMaterialLocalizations(context)); - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); final _DropdownRoute route = widget.route; final List children = [ for (int itemIndex = 0; itemIndex < route.items.length; ++itemIndex) @@ -1216,7 +1216,7 @@ class _DropdownButtonState extends State> with WidgetsBindi elevation: widget.elevation, theme: Theme.of(context, shadowThemeOnly: true), style: _textStyle!, - barrierLabel: MaterialLocalizations.of(context)!.modalBarrierDismissLabel, + barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, itemHeight: widget.itemHeight, dropdownColor: widget.dropdownColor, ); diff --git a/packages/flutter/lib/src/material/expand_icon.dart b/packages/flutter/lib/src/material/expand_icon.dart index 6bf19daaaa..934ff489d8 100644 --- a/packages/flutter/lib/src/material/expand_icon.dart +++ b/packages/flutter/lib/src/material/expand_icon.dart @@ -167,7 +167,7 @@ class _ExpandIconState extends State with SingleTickerProviderStateM Widget build(BuildContext context) { assert(debugCheckHasMaterial(context)); assert(debugCheckHasMaterialLocalizations(context)); - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); final String onTapHint = widget.isExpanded ? localizations.expandedIconTapHint : localizations.collapsedIconTapHint; return Semantics( diff --git a/packages/flutter/lib/src/material/expansion_panel.dart b/packages/flutter/lib/src/material/expansion_panel.dart index 7658c6fbaa..7731324c3a 100644 --- a/packages/flutter/lib/src/material/expansion_panel.dart +++ b/packages/flutter/lib/src/material/expansion_panel.dart @@ -496,7 +496,7 @@ class _ExpansionPanelListState extends State { ), ); if (!child.canTapOnHeader) { - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); expandIconContainer = Semantics( label: _isChildExpanded(index)? localizations.expandedIconTapHint : localizations.collapsedIconTapHint, container: true, diff --git a/packages/flutter/lib/src/material/material_localizations.dart b/packages/flutter/lib/src/material/material_localizations.dart index 0c613f3461..5af8df6b40 100644 --- a/packages/flutter/lib/src/material/material_localizations.dart +++ b/packages/flutter/lib/src/material/material_localizations.dart @@ -5,8 +5,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; -import 'reorderable_list.dart'; -import 'text_theme.dart'; +import 'debug.dart'; import 'time.dart'; import 'typography.dart'; @@ -494,8 +493,11 @@ abstract class MaterialLocalizations { /// The `MaterialLocalizations` from the closest [Localizations] instance /// that encloses the given context. /// + /// If no [MaterialLocalizations] are available in the given `context`, this + /// method throws an exception. + /// /// This method is just a convenient shorthand for: - /// `Localizations.of(context, MaterialLocalizations)`. + /// `Localizations.of(context, MaterialLocalizations)!`. /// /// References to the localized resources defined by this class are typically /// written in terms of this method. For example: @@ -503,8 +505,9 @@ abstract class MaterialLocalizations { /// ```dart /// tooltip: MaterialLocalizations.of(context).backButtonTooltip, /// ``` - static MaterialLocalizations? of(BuildContext context) { - return Localizations.of(context, MaterialLocalizations); + static MaterialLocalizations of(BuildContext context) { + debugCheckHasMaterialLocalizations(context); + return Localizations.of(context, MaterialLocalizations)!; } } diff --git a/packages/flutter/lib/src/material/navigation_rail.dart b/packages/flutter/lib/src/material/navigation_rail.dart index 58290242ab..be2a01f92b 100644 --- a/packages/flutter/lib/src/material/navigation_rail.dart +++ b/packages/flutter/lib/src/material/navigation_rail.dart @@ -433,7 +433,7 @@ class _NavigationRailState extends State with TickerProviderStat Widget build(BuildContext context) { final ThemeData theme = Theme.of(context)!; final NavigationRailThemeData navigationRailTheme = NavigationRailTheme.of(context); - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); final Color backgroundColor = widget.backgroundColor ?? navigationRailTheme.backgroundColor ?? theme.colorScheme.surface; final double elevation = widget.elevation ?? navigationRailTheme.elevation ?? 0; diff --git a/packages/flutter/lib/src/material/paginated_data_table.dart b/packages/flutter/lib/src/material/paginated_data_table.dart index f594823da2..1402eeb729 100644 --- a/packages/flutter/lib/src/material/paginated_data_table.dart +++ b/packages/flutter/lib/src/material/paginated_data_table.dart @@ -333,7 +333,7 @@ class PaginatedDataTableState extends State { // TODO(ianh): This whole build function doesn't handle RTL yet. assert(debugCheckHasMaterialLocalizations(context)); final ThemeData themeData = Theme.of(context)!; - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); // HEADER final List headerWidgets = []; double startPadding = 24.0; diff --git a/packages/flutter/lib/src/material/pickers/calendar_date_picker.dart b/packages/flutter/lib/src/material/pickers/calendar_date_picker.dart index 12837741d9..858136436a 100644 --- a/packages/flutter/lib/src/material/pickers/calendar_date_picker.dart +++ b/packages/flutter/lib/src/material/pickers/calendar_date_picker.dart @@ -179,7 +179,7 @@ class _CalendarDatePickerState extends State { assert(debugCheckHasMaterial(context)); assert(debugCheckHasMaterialLocalizations(context)); assert(debugCheckHasDirectionality(context)); - _localizations = MaterialLocalizations.of(context)!; + _localizations = MaterialLocalizations.of(context); _textDirection = Directionality.of(context)!; if (!_announcedInitialDate) { _announcedInitialDate = true; @@ -380,7 +380,7 @@ class _DatePickerModeToggleButtonState extends State<_DatePickerModeToggleButton children: [ Flexible( child: Semantics( - label: MaterialLocalizations.of(context)!.selectYearSemanticsLabel, + label: MaterialLocalizations.of(context).selectYearSemanticsLabel, excludeSemantics: true, button: true, child: Container( @@ -524,7 +524,7 @@ class _MonthPickerState extends State<_MonthPicker> { @override void didChangeDependencies() { super.didChangeDependencies(); - _localizations = MaterialLocalizations.of(context)!; + _localizations = MaterialLocalizations.of(context); _textDirection = Directionality.of(context); } @@ -941,7 +941,7 @@ class _DayPickerState extends State<_DayPicker> { @override Widget build(BuildContext context) { final ColorScheme colorScheme = Theme.of(context)!.colorScheme; - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); final TextTheme textTheme = Theme.of(context)!.textTheme; final TextStyle? headerStyle = textTheme.caption?.apply( color: colorScheme.onSurface.withOpacity(0.60), diff --git a/packages/flutter/lib/src/material/pickers/calendar_date_range_picker.dart b/packages/flutter/lib/src/material/pickers/calendar_date_range_picker.dart index f92f45c5b7..e175db1ece 100644 --- a/packages/flutter/lib/src/material/pickers/calendar_date_range_picker.dart +++ b/packages/flutter/lib/src/material/pickers/calendar_date_range_picker.dart @@ -427,7 +427,7 @@ class _DayHeaders extends StatelessWidget { final ThemeData themeData = Theme.of(context)!; final ColorScheme colorScheme = themeData.colorScheme; final TextStyle textStyle = themeData.textTheme.subtitle2!.apply(color: colorScheme.onSurface); - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); final List labels = _getDayHeaders(textStyle, localizations); // Add leading and trailing containers for edges of the custom grid layout. @@ -706,7 +706,7 @@ class _MonthItemState extends State<_MonthItem> { final ThemeData theme = Theme.of(context)!; final ColorScheme colorScheme = theme.colorScheme; final TextTheme textTheme = theme.textTheme; - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); final TextDirection? textDirection = Directionality.of(context); final Color highlightColor = _highlightColor(context); final int day = dayToBuild.day; @@ -818,7 +818,7 @@ class _MonthItemState extends State<_MonthItem> { Widget build(BuildContext context) { final ThemeData themeData = Theme.of(context)!; final TextTheme textTheme = themeData.textTheme; - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); final int year = widget.displayedMonth.year; final int month = widget.displayedMonth.month; final int daysInMonth = utils.getDaysInMonth(year, month); diff --git a/packages/flutter/lib/src/material/pickers/date_picker_deprecated.dart b/packages/flutter/lib/src/material/pickers/date_picker_deprecated.dart index dfbb205e28..e75f169bd9 100644 --- a/packages/flutter/lib/src/material/pickers/date_picker_deprecated.dart +++ b/packages/flutter/lib/src/material/pickers/date_picker_deprecated.dart @@ -239,7 +239,7 @@ class DayPicker extends StatelessWidget { @override Widget build(BuildContext context) { final ThemeData themeData = Theme.of(context)!; - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); final int year = displayedMonth.year; final int month = displayedMonth.month; final int daysInMonth = getDaysInMonth(year, month); diff --git a/packages/flutter/lib/src/material/pickers/date_picker_dialog.dart b/packages/flutter/lib/src/material/pickers/date_picker_dialog.dart index 5c1142717d..831c5f8662 100644 --- a/packages/flutter/lib/src/material/pickers/date_picker_dialog.dart +++ b/packages/flutter/lib/src/material/pickers/date_picker_dialog.dart @@ -359,7 +359,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> { Widget build(BuildContext context) { final ThemeData theme = Theme.of(context)!; final ColorScheme colorScheme = theme.colorScheme; - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); final Orientation orientation = MediaQuery.of(context)!.orientation; final TextTheme textTheme = theme.textTheme; // Constrain the textScaleFactor to the largest supported value to prevent diff --git a/packages/flutter/lib/src/material/pickers/date_range_picker_dialog.dart b/packages/flutter/lib/src/material/pickers/date_range_picker_dialog.dart index 9b5ce429de..b20000ced7 100644 --- a/packages/flutter/lib/src/material/pickers/date_range_picker_dialog.dart +++ b/packages/flutter/lib/src/material/pickers/date_range_picker_dialog.dart @@ -338,7 +338,7 @@ class _DateRangePickerDialogState extends State<_DateRangePickerDialog> { final MediaQueryData mediaQuery = MediaQuery.of(context)!; final Orientation orientation = mediaQuery.orientation; final double textScaleFactor = math.min(mediaQuery.textScaleFactor, 1.3); - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); final Widget contents; final Size size; @@ -478,7 +478,7 @@ class _CalendarRangePickerDialog extends StatelessWidget { Widget build(BuildContext context) { final ThemeData theme = Theme.of(context)!; final ColorScheme colorScheme = theme.colorScheme; - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); final Orientation orientation = MediaQuery.of(context)!.orientation; final TextTheme textTheme = theme.textTheme; final Color headerForeground = colorScheme.brightness == Brightness.light @@ -615,7 +615,7 @@ class _InputDateRangePickerDialog extends StatelessWidget { final String? helpText; String _formatDateRange(BuildContext context, DateTime? start, DateTime? end, DateTime now) { - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); final String startText = utils.formatRangeStartDate(localizations, start, end); final String endText = utils.formatRangeEndDate(localizations, start, end, now); if (start == null || end == null) { @@ -632,7 +632,7 @@ class _InputDateRangePickerDialog extends StatelessWidget { Widget build(BuildContext context) { final ThemeData theme = Theme.of(context)!; final ColorScheme colorScheme = theme.colorScheme; - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); final Orientation orientation = MediaQuery.of(context)!.orientation; final TextTheme textTheme = theme.textTheme; diff --git a/packages/flutter/lib/src/material/pickers/input_date_picker.dart b/packages/flutter/lib/src/material/pickers/input_date_picker.dart index b3345b2d69..8af8db6b92 100644 --- a/packages/flutter/lib/src/material/pickers/input_date_picker.dart +++ b/packages/flutter/lib/src/material/pickers/input_date_picker.dart @@ -157,7 +157,7 @@ class _InputDatePickerFormFieldState extends State { void didChangeDependencies() { super.didChangeDependencies(); if (_selectedDate != null) { - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); _inputText = localizations.formatCompactDate(_selectedDate!); TextEditingValue textEditingValue = _controller.value.copyWith(text: _inputText); // Select the new text if we are auto focused and haven't selected the text before. @@ -173,7 +173,7 @@ class _InputDatePickerFormFieldState extends State { } DateTime? _parseDate(String? text) { - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); return localizations.parseCompactDate(text); } @@ -188,9 +188,9 @@ class _InputDatePickerFormFieldState extends State { String? _validateDate(String? text) { final DateTime? date = _parseDate(text); if (date == null) { - return widget.errorFormatText ?? MaterialLocalizations.of(context)!.invalidDateFormatLabel; + return widget.errorFormatText ?? MaterialLocalizations.of(context).invalidDateFormatLabel; } else if (!_isValidAcceptableDate(date)) { - return widget.errorInvalidText ?? MaterialLocalizations.of(context)!.dateOutOfRangeLabel; + return widget.errorInvalidText ?? MaterialLocalizations.of(context).dateOutOfRangeLabel; } return null; } @@ -219,7 +219,7 @@ class _InputDatePickerFormFieldState extends State { @override Widget build(BuildContext context) { - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); final InputDecorationTheme inputTheme = Theme.of(context)!.inputDecorationTheme; return TextFormField( decoration: InputDecoration( diff --git a/packages/flutter/lib/src/material/pickers/input_date_range_picker.dart b/packages/flutter/lib/src/material/pickers/input_date_range_picker.dart index bd11184105..4331e70fa5 100644 --- a/packages/flutter/lib/src/material/pickers/input_date_range_picker.dart +++ b/packages/flutter/lib/src/material/pickers/input_date_range_picker.dart @@ -141,7 +141,7 @@ class InputDateRangePickerState extends State { @override void didChangeDependencies() { super.didChangeDependencies(); - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); if (_startDate != null) { _startInputText = localizations.formatCompactDate(_startDate!); final bool selectText = widget.autofocus && !_autoSelected; @@ -166,7 +166,7 @@ class InputDateRangePickerState extends State { final String? endError = _validateDate(_endDate); if (startError == null && endError == null) { if (_startDate!.isAfter(_endDate!)) { - startError = widget.errorInvalidRangeText ?? MaterialLocalizations.of(context)!.invalidDateRangeLabel; + startError = widget.errorInvalidRangeText ?? MaterialLocalizations.of(context).invalidDateRangeLabel; } } setState(() { @@ -177,15 +177,15 @@ class InputDateRangePickerState extends State { } DateTime? _parseDate(String? text) { - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); return localizations.parseCompactDate(text); } String? _validateDate(DateTime? date) { if (date == null) { - return widget.errorFormatText ?? MaterialLocalizations.of(context)!.invalidDateFormatLabel; + return widget.errorFormatText ?? MaterialLocalizations.of(context).invalidDateFormatLabel; } else if (date.isBefore(widget.firstDate) || date.isAfter(widget.lastDate)) { - return widget.errorInvalidText ?? MaterialLocalizations.of(context)!.dateOutOfRangeLabel; + return widget.errorInvalidText ?? MaterialLocalizations.of(context).dateOutOfRangeLabel; } return null; } @@ -225,7 +225,7 @@ class InputDateRangePickerState extends State { @override Widget build(BuildContext context) { - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); final InputDecorationTheme inputTheme = Theme.of(context)!.inputDecorationTheme; return Row( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/packages/flutter/lib/src/material/popup_menu.dart b/packages/flutter/lib/src/material/popup_menu.dart index 7c4df124ff..69d6f5b703 100644 --- a/packages/flutter/lib/src/material/popup_menu.dart +++ b/packages/flutter/lib/src/material/popup_menu.dart @@ -848,17 +848,15 @@ Future showMenu({ assert(captureInheritedThemes != null); assert(debugCheckHasMaterialLocalizations(context)); - String? label; switch (Theme.of(context)!.platform) { case TargetPlatform.iOS: case TargetPlatform.macOS: - label = semanticLabel; break; case TargetPlatform.android: case TargetPlatform.fuchsia: case TargetPlatform.linux: case TargetPlatform.windows: - label = semanticLabel ?? MaterialLocalizations.of(context)?.popupMenuLabel; + semanticLabel ??= MaterialLocalizations.of(context).popupMenuLabel; } return Navigator.of(context, rootNavigator: useRootNavigator)!.push(_PopupMenuRoute( @@ -866,10 +864,10 @@ Future showMenu({ items: items, initialValue: initialValue, elevation: elevation, - semanticLabel: label, + semanticLabel: semanticLabel, theme: Theme.of(context, shadowThemeOnly: true), popupMenuTheme: PopupMenuTheme.of(context), - barrierLabel: MaterialLocalizations.of(context)!.modalBarrierDismissLabel, + barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, shape: shape, color: color, showMenuContext: context, @@ -1142,7 +1140,7 @@ class PopupMenuButtonState extends State> { if (widget.child != null) return Tooltip( - message: widget.tooltip ?? MaterialLocalizations.of(context)!.showMenuTooltip, + message: widget.tooltip ?? MaterialLocalizations.of(context).showMenuTooltip, child: InkWell( onTap: widget.enabled ? showButtonMenu : null, canRequestFocus: _canRequestFocus, @@ -1153,7 +1151,7 @@ class PopupMenuButtonState extends State> { return IconButton( icon: widget.icon ?? _getIcon(Theme.of(context)!.platform), padding: widget.padding, - tooltip: widget.tooltip ?? MaterialLocalizations.of(context)!.showMenuTooltip, + tooltip: widget.tooltip ?? MaterialLocalizations.of(context).showMenuTooltip, onPressed: widget.enabled ? showButtonMenu : null, ); } diff --git a/packages/flutter/lib/src/material/refresh_indicator.dart b/packages/flutter/lib/src/material/refresh_indicator.dart index 55b8b9a14f..71ee331b86 100644 --- a/packages/flutter/lib/src/material/refresh_indicator.dart +++ b/packages/flutter/lib/src/material/refresh_indicator.dart @@ -467,7 +467,7 @@ class RefreshIndicatorState extends State with TickerProviderS animation: _positionController, builder: (BuildContext context, Widget? child) { return RefreshProgressIndicator( - semanticsLabel: widget.semanticsLabel ?? MaterialLocalizations.of(context)!.refreshIndicatorSemanticLabel, + semanticsLabel: widget.semanticsLabel ?? MaterialLocalizations.of(context).refreshIndicatorSemanticLabel, semanticsValue: widget.semanticsValue, value: showIndeterminateIndicator ? null : _value.value, valueColor: _valueColor, diff --git a/packages/flutter/lib/src/material/reorderable_list.dart b/packages/flutter/lib/src/material/reorderable_list.dart index d20c693eea..7d1851ef68 100644 --- a/packages/flutter/lib/src/material/reorderable_list.dart +++ b/packages/flutter/lib/src/material/reorderable_list.dart @@ -402,7 +402,7 @@ class _ReorderableListContentState extends State<_ReorderableListContent> with T // before index+2, which is after the space at index+1. void moveAfter() => reorder(index, index + 2); - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); // If the item can move to before its current position in the list. if (index > 0) { diff --git a/packages/flutter/lib/src/material/search.dart b/packages/flutter/lib/src/material/search.dart index e4e8996a60..be28a4ab34 100644 --- a/packages/flutter/lib/src/material/search.dart +++ b/packages/flutter/lib/src/material/search.dart @@ -479,7 +479,7 @@ class _SearchPageState extends State<_SearchPage> { assert(debugCheckHasMaterialLocalizations(context)); final ThemeData theme = widget.delegate.appBarTheme(context); final String searchFieldLabel = widget.delegate.searchFieldLabel - ?? MaterialLocalizations.of(context)!.searchFieldLabel; + ?? MaterialLocalizations.of(context).searchFieldLabel; final TextStyle? searchFieldStyle = widget.delegate.searchFieldStyle ?? theme.inputDecorationTheme.hintStyle; Widget? body; diff --git a/packages/flutter/lib/src/material/stepper.dart b/packages/flutter/lib/src/material/stepper.dart index 185b79c483..1057196ae4 100644 --- a/packages/flutter/lib/src/material/stepper.dart +++ b/packages/flutter/lib/src/material/stepper.dart @@ -406,7 +406,7 @@ class _StepperState extends State with TickerProviderStateMixin { final ThemeData themeData = Theme.of(context)!; final ColorScheme colorScheme = themeData.colorScheme; - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); const OutlinedBorder buttonShape = RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(2))); const EdgeInsets buttonPadding = EdgeInsets.symmetric(horizontal: 16.0); diff --git a/packages/flutter/lib/src/material/tabs.dart b/packages/flutter/lib/src/material/tabs.dart index 024305eac3..daabd26fb7 100644 --- a/packages/flutter/lib/src/material/tabs.dart +++ b/packages/flutter/lib/src/material/tabs.dart @@ -1030,7 +1030,7 @@ class _TabBarState extends State { } return true; }()); - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); if (_controller!.length == 0) { return Container( height: _kTabHeight + widget.indicatorWeight, diff --git a/packages/flutter/lib/src/material/text_field.dart b/packages/flutter/lib/src/material/text_field.dart index 91f8dde9b8..2be2fe7986 100644 --- a/packages/flutter/lib/src/material/text_field.dart +++ b/packages/flutter/lib/src/material/text_field.dart @@ -865,7 +865,7 @@ class _TextFieldState extends State with RestorationMixin implements bool get _hasError => widget.decoration?.errorText != null || _hasIntrinsicError; InputDecoration _getEffectiveDecoration() { - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); final ThemeData themeData = Theme.of(context)!; final InputDecoration effectiveDecoration = (widget.decoration ?? const InputDecoration()) .applyDefaults(themeData.inputDecorationTheme) diff --git a/packages/flutter/lib/src/material/text_selection.dart b/packages/flutter/lib/src/material/text_selection.dart index ffbb1f8fee..620d26e1b4 100644 --- a/packages/flutter/lib/src/material/text_selection.dart +++ b/packages/flutter/lib/src/material/text_selection.dart @@ -183,7 +183,7 @@ class _TextSelectionToolbarState extends State<_TextSelectionToolbar> with Ticke return const SizedBox(width: 0.0, height: 0.0); } - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); final List<_ItemData> itemDatas = <_ItemData>[ if (widget.handleCut != null) _ItemData(widget.handleCut!, localizations.cutButtonLabel), diff --git a/packages/flutter/lib/src/material/theme.dart b/packages/flutter/lib/src/material/theme.dart index 47691962f6..c363e71606 100644 --- a/packages/flutter/lib/src/material/theme.dart +++ b/packages/flutter/lib/src/material/theme.dart @@ -132,7 +132,7 @@ class Theme extends StatelessWidget { return inheritedTheme.theme.data; } - final MaterialLocalizations? localizations = MaterialLocalizations.of(context); + final MaterialLocalizations? localizations = Localizations.of(context, MaterialLocalizations); final ScriptCategory category = localizations?.scriptCategory ?? ScriptCategory.englishLike; final ThemeData theme = inheritedTheme?.theme.data ?? _kFallbackTheme; return ThemeData.localize(theme, theme.typography.geometryThemeFor(category)); diff --git a/packages/flutter/lib/src/material/time.dart b/packages/flutter/lib/src/material/time.dart index a3599742fa..91953f2914 100644 --- a/packages/flutter/lib/src/material/time.dart +++ b/packages/flutter/lib/src/material/time.dart @@ -104,7 +104,7 @@ class TimeOfDay { String format(BuildContext context) { assert(debugCheckHasMediaQuery(context)); assert(debugCheckHasMaterialLocalizations(context)); - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); return localizations.formatTimeOfDay( this, alwaysUse24HourFormat: MediaQuery.of(context)!.alwaysUse24HourFormat, diff --git a/packages/flutter/lib/src/material/time_picker.dart b/packages/flutter/lib/src/material/time_picker.dart index 1b465e5241..dd2bfc29e4 100644 --- a/packages/flutter/lib/src/material/time_picker.dart +++ b/packages/flutter/lib/src/material/time_picker.dart @@ -134,7 +134,7 @@ class _TimePickerHeader extends StatelessWidget { Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); final ThemeData themeData = Theme.of(context)!; - final TimeOfDayFormat timeOfDayFormat = MaterialLocalizations.of(context)!.timeOfDayFormat( + final TimeOfDayFormat timeOfDayFormat = MaterialLocalizations.of(context).timeOfDayFormat( alwaysUse24HourFormat: MediaQuery.of(context)!.alwaysUse24HourFormat, ); @@ -241,7 +241,7 @@ class _TimePickerHeader extends StatelessWidget { children: [ const SizedBox(height: 16.0), Text( - helpText ?? MaterialLocalizations.of(context)!.timePickerDialHelpText, + helpText ?? MaterialLocalizations.of(context).timePickerDialHelpText, style: TimePickerTheme.of(context).helpTextStyle ?? themeData.textTheme.overline, ), controls, @@ -322,7 +322,7 @@ class _HourControl extends StatelessWidget { Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); final bool alwaysUse24HourFormat = MediaQuery.of(context)!.alwaysUse24HourFormat; - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); final String formattedHour = localizations.formatHour( fragmentContext.selectedTime, alwaysUse24HourFormat: alwaysUse24HourFormat, @@ -432,7 +432,7 @@ class _MinuteControl extends StatelessWidget { @override Widget build(BuildContext context) { - final MaterialLocalizations localizations = MaterialLocalizations.of(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, @@ -493,7 +493,7 @@ class _DayPeriodControl extends StatelessWidget { case TargetPlatform.fuchsia: case TargetPlatform.linux: case TargetPlatform.windows: - _announceToAccessibility(context, MaterialLocalizations.of(context)!.anteMeridiemAbbreviation); + _announceToAccessibility(context, MaterialLocalizations.of(context).anteMeridiemAbbreviation); break; case TargetPlatform.iOS: case TargetPlatform.macOS: @@ -511,7 +511,7 @@ class _DayPeriodControl extends StatelessWidget { case TargetPlatform.fuchsia: case TargetPlatform.linux: case TargetPlatform.windows: - _announceToAccessibility(context, MaterialLocalizations.of(context)!.postMeridiemAbbreviation); + _announceToAccessibility(context, MaterialLocalizations.of(context).postMeridiemAbbreviation); break; case TargetPlatform.iOS: case TargetPlatform.macOS: @@ -522,7 +522,7 @@ class _DayPeriodControl extends StatelessWidget { @override Widget build(BuildContext context) { - final MaterialLocalizations materialLocalizations = MaterialLocalizations.of(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; @@ -941,7 +941,7 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin { super.didChangeDependencies(); assert(debugCheckHasMediaQuery(context)); themeData = Theme.of(context)!; - localizations = MaterialLocalizations.of(context)!; + localizations = MaterialLocalizations.of(context); media = MediaQuery.of(context)!; } @@ -1409,7 +1409,7 @@ class _TimePickerInputState extends State<_TimePickerInput> { 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: media.alwaysUse24HourFormat); final bool use24HourDials = hourFormat(of: timeOfDayFormat) != HourFormat.h; final ThemeData theme = Theme.of(context)!; final TextStyle hourMinuteStyle = TimePickerTheme.of(context).hourMinuteTextStyle ?? theme.textTheme.headline2!; @@ -1420,7 +1420,7 @@ class _TimePickerInputState extends State<_TimePickerInput> { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - widget.helpText ?? MaterialLocalizations.of(context)!.timePickerInputHelpText, + widget.helpText ?? MaterialLocalizations.of(context).timePickerInputHelpText, style: TimePickerTheme.of(context).helpTextStyle ?? theme.textTheme.overline, ), const SizedBox(height: 16.0), @@ -1458,7 +1458,7 @@ class _TimePickerInputState extends State<_TimePickerInput> { if (!hourHasError && !minuteHasError) ExcludeSemantics( child: Text( - MaterialLocalizations.of(context)!.timePickerHourLabel, + MaterialLocalizations.of(context).timePickerHourLabel, style: theme.textTheme.caption, maxLines: 1, overflow: TextOverflow.ellipsis, @@ -1488,7 +1488,7 @@ class _TimePickerInputState extends State<_TimePickerInput> { if (!hourHasError && !minuteHasError) ExcludeSemantics( child: Text( - MaterialLocalizations.of(context)!.timePickerMinuteLabel, + MaterialLocalizations.of(context).timePickerMinuteLabel, style: theme.textTheme.caption, maxLines: 1, overflow: TextOverflow.ellipsis, @@ -1512,7 +1512,7 @@ class _TimePickerInputState extends State<_TimePickerInput> { ), if (hourHasError || minuteHasError) Text( - MaterialLocalizations.of(context)!.invalidTimeLabel, + MaterialLocalizations.of(context).invalidTimeLabel, style: theme.textTheme.bodyText2!.copyWith(color: theme.colorScheme.error), ) else @@ -1548,7 +1548,7 @@ class _HourTextField extends StatelessWidget { isHour: true, autofocus: autofocus, style: style, - semanticHintText: MaterialLocalizations.of(context)!.timePickerHourLabel, + semanticHintText: MaterialLocalizations.of(context).timePickerHourLabel, validator: validator, onSavedSubmitted: onSavedSubmitted, onChanged: onChanged, @@ -1579,7 +1579,7 @@ class _MinuteTextField extends StatelessWidget { isHour: false, autofocus: autofocus, style: style, - semanticHintText: MaterialLocalizations.of(context)!.timePickerMinuteLabel, + semanticHintText: MaterialLocalizations.of(context).timePickerMinuteLabel, validator: validator, onSavedSubmitted: onSavedSubmitted, ); @@ -1632,7 +1632,7 @@ class _HourMinuteTextFieldState extends State<_HourMinuteTextField> { String get _formattedValue { final bool alwaysUse24HourFormat = MediaQuery.of(context)!.alwaysUse24HourFormat; - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); return !widget.isHour ? localizations.formatMinute(widget.selectedTime) : localizations.formatHour( widget.selectedTime, alwaysUse24HourFormat: alwaysUse24HourFormat, @@ -1770,7 +1770,7 @@ class _TimePickerDialogState extends State<_TimePickerDialog> { @override void didChangeDependencies() { super.didChangeDependencies(); - localizations = MaterialLocalizations.of(context)!; + localizations = MaterialLocalizations.of(context); _announceInitialTimeOnce(); _announceModeOnce(); } @@ -1855,7 +1855,7 @@ class _TimePickerDialogState extends State<_TimePickerDialog> { return; final MediaQueryData media = MediaQuery.of(context)!; - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); _announceToAccessibility( context, localizations.formatTimeOfDay(widget.initialTime, alwaysUse24HourFormat: media.alwaysUse24HourFormat), @@ -1957,8 +1957,8 @@ class _TimePickerDialogState extends State<_TimePickerDialog> { onPressed: _handleEntryModeToggle, icon: Icon(_entryMode == TimePickerEntryMode.dial ? Icons.keyboard : Icons.access_time), tooltip: _entryMode == TimePickerEntryMode.dial - ? MaterialLocalizations.of(context)!.inputTimeModeButtonLabel - : MaterialLocalizations.of(context)!.dialModeButtonLabel, + ? MaterialLocalizations.of(context).inputTimeModeButtonLabel + : MaterialLocalizations.of(context).dialModeButtonLabel, ), Expanded( child: Container( diff --git a/packages/flutter/lib/src/material/user_accounts_drawer_header.dart b/packages/flutter/lib/src/material/user_accounts_drawer_header.dart index 28e97be763..ed74e0a377 100644 --- a/packages/flutter/lib/src/material/user_accounts_drawer_header.dart +++ b/packages/flutter/lib/src/material/user_accounts_drawer_header.dart @@ -134,7 +134,7 @@ class _AccountDetailsState extends State<_AccountDetails> with SingleTickerProvi assert(debugCheckHasMaterialLocalizations(context)); final ThemeData theme = Theme.of(context)!; - final MaterialLocalizations localizations = MaterialLocalizations.of(context)!; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); Widget accountDetails = CustomMultiChildLayout( delegate: _AccountDetailsLayout( @@ -356,7 +356,7 @@ class _UserAccountsDrawerHeaderState extends State { assert(debugCheckHasMaterialLocalizations(context)); return Semantics( container: true, - label: MaterialLocalizations.of(context)!.signedInLabel, + label: MaterialLocalizations.of(context).signedInLabel, child: DrawerHeader( decoration: widget.decoration ?? BoxDecoration( color: Theme.of(context)!.primaryColor, diff --git a/packages/flutter/test/cupertino/app_test.dart b/packages/flutter/test/cupertino/app_test.dart index 24c68609c9..f37db85734 100644 --- a/packages/flutter/test/cupertino/app_test.dart +++ b/packages/flutter/test/cupertino/app_test.dart @@ -50,8 +50,8 @@ void main() { builder: (BuildContext context) { return Column( children: [ - Text(CupertinoLocalizations.of(context)!.selectAllButtonLabel), - Text(CupertinoLocalizations.of(context)!.datePickerMediumDate( + Text(CupertinoLocalizations.of(context).selectAllButtonLabel), + Text(CupertinoLocalizations.of(context).datePickerMediumDate( DateTime(2018, 10, 4), )), ], diff --git a/packages/flutter/test/cupertino/debug_test.dart b/packages/flutter/test/cupertino/debug_test.dart new file mode 100644 index 0000000000..f69789f142 --- /dev/null +++ b/packages/flutter/test/cupertino/debug_test.dart @@ -0,0 +1,33 @@ +// 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/cupertino.dart'; +import 'package:flutter/src/cupertino/debug.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('debugCheckHasCupertinoLocalizations throws', (WidgetTester tester) async { + final GlobalKey noLocalizationsAvailable = GlobalKey(); + final GlobalKey localizationsAvailable = GlobalKey(); + + await tester.pumpWidget( + Container( + key: noLocalizationsAvailable, + child: CupertinoApp( + home: Container( + key: localizationsAvailable, + ), + ), + ), + ); + + expect(() => debugCheckHasCupertinoLocalizations(noLocalizationsAvailable.currentContext!), throwsA(isAssertionError.having( + (AssertionError e) => e.message, + 'message', + contains('No CupertinoLocalizations found'), + ))); + + expect(debugCheckHasCupertinoLocalizations(localizationsAvailable.currentContext!), isTrue); + }); +} diff --git a/packages/flutter/test/cupertino/localizations_test.dart b/packages/flutter/test/cupertino/localizations_test.dart index e87d290527..4399e543e6 100644 --- a/packages/flutter/test/cupertino/localizations_test.dart +++ b/packages/flutter/test/cupertino/localizations_test.dart @@ -32,4 +32,28 @@ void main() { expect(localizations.modalBarrierDismissLabel, isNotNull); }); + + testWidgets('CupertinoLocalizations.of throws', (WidgetTester tester) async { + final GlobalKey noLocalizationsAvailable = GlobalKey(); + final GlobalKey localizationsAvailable = GlobalKey(); + + await tester.pumpWidget( + Container( + key: noLocalizationsAvailable, + child: CupertinoApp( + home: Container( + key: localizationsAvailable, + ), + ), + ), + ); + + expect(() => CupertinoLocalizations.of(noLocalizationsAvailable.currentContext!), throwsA(isAssertionError.having( + (AssertionError e) => e.message, + 'message', + contains('No CupertinoLocalizations found'), + ))); + + expect(CupertinoLocalizations.of(localizationsAvailable.currentContext!), isA()); + }); } diff --git a/packages/flutter/test/material/app_test.dart b/packages/flutter/test/material/app_test.dart index 46fbcecf0c..1d65595ced 100644 --- a/packages/flutter/test/material/app_test.dart +++ b/packages/flutter/test/material/app_test.dart @@ -500,8 +500,8 @@ void main() { builder: (BuildContext context) { return Column( children: [ - Text(MaterialLocalizations.of(context)!.selectAllButtonLabel), - Text(CupertinoLocalizations.of(context)!.selectAllButtonLabel), + Text(MaterialLocalizations.of(context).selectAllButtonLabel), + Text(CupertinoLocalizations.of(context).selectAllButtonLabel), ], ); }, diff --git a/packages/flutter/test/material/localizations_test.dart b/packages/flutter/test/material/localizations_test.dart index d484e161f9..b05305ac4a 100644 --- a/packages/flutter/test/material/localizations_test.dart +++ b/packages/flutter/test/material/localizations_test.dart @@ -102,4 +102,28 @@ void main() { expect(localizations.licensesPackageDetailText(2).contains(r'$licensesCount'), isFalse); expect(localizations.licensesPackageDetailText(100).contains(r'$licensesCount'), isFalse); }); + + testWidgets('MaterialLocalizations.of throws', (WidgetTester tester) async { + final GlobalKey noLocalizationsAvailable = GlobalKey(); + final GlobalKey localizationsAvailable = GlobalKey(); + + await tester.pumpWidget( + Container( + key: noLocalizationsAvailable, + child: MaterialApp( + home: Container( + key: localizationsAvailable, + ), + ), + ), + ); + + expect(() => MaterialLocalizations.of(noLocalizationsAvailable.currentContext!), throwsA(isAssertionError.having( + (AssertionError e) => e.message, + 'message', + contains('No MaterialLocalizations found'), + ))); + + expect(MaterialLocalizations.of(localizationsAvailable.currentContext!), isA()); + }); } diff --git a/packages/flutter/test/material/time_picker_test.dart b/packages/flutter/test/material/time_picker_test.dart index cc13240745..891398d941 100644 --- a/packages/flutter/test/material/time_picker_test.dart +++ b/packages/flutter/test/material/time_picker_test.dart @@ -65,7 +65,7 @@ Future startPicker( } 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)); }