Skip to content

Commit

Permalink
Add debug check for localization parent (flutter#20787)
Browse files Browse the repository at this point in the history
  • Loading branch information
jonahwilliams authored Sep 13, 2018
1 parent 199422c commit 9bf8502
Show file tree
Hide file tree
Showing 26 changed files with 621 additions and 354 deletions.
3 changes: 3 additions & 0 deletions packages/flutter/lib/src/material/about.dart
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ class AboutListTile extends StatelessWidget {
@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context));
assert(debugCheckHasMaterialLocalizations(context));
return ListTile(
leading: icon,
title: child ??
Expand Down Expand Up @@ -263,6 +264,7 @@ class AboutDialog extends StatelessWidget {

@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context));
final String name = applicationName ?? _defaultApplicationName(context);
final String version = applicationVersion ?? _defaultApplicationVersion(context);
final Widget icon = applicationIcon ?? _defaultApplicationIcon(context);
Expand Down Expand Up @@ -437,6 +439,7 @@ class _LicensePageState extends State<LicensePage> {

@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context));
final String name = widget.applicationName ?? _defaultApplicationName(context);
final String version = widget.applicationVersion ?? _defaultApplicationVersion(context);
final MaterialLocalizations localizations = MaterialLocalizations.of(context);
Expand Down
2 changes: 2 additions & 0 deletions packages/flutter/lib/src/material/app_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:flutter/widgets.dart';

import 'back_button.dart';
import 'constants.dart';
import 'debug.dart';
import 'flexible_space_bar.dart';
import 'icon_button.dart';
import 'icons.dart';
Expand Down Expand Up @@ -332,6 +333,7 @@ class _AppBarState extends State<AppBar> {
@override
Widget build(BuildContext context) {
assert(!widget.primary || debugCheckHasMediaQuery(context));
assert(debugCheckHasMaterialLocalizations(context));
final ThemeData themeData = Theme.of(context);
final ScaffoldState scaffold = Scaffold.of(context, nullOk: true);
final ModalRoute<dynamic> parentRoute = ModalRoute.of(context);
Expand Down
2 changes: 2 additions & 0 deletions packages/flutter/lib/src/material/back_button.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import 'package:flutter/widgets.dart';

import 'debug.dart';
import 'icon_button.dart';
import 'icons.dart';
import 'material_localizations.dart';
Expand Down Expand Up @@ -81,6 +82,7 @@ class BackButton extends StatelessWidget {

@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context));
return IconButton(
icon: const BackButtonIcon(),
color: color,
Expand Down
2 changes: 2 additions & 0 deletions packages/flutter/lib/src/material/bottom_navigation_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:vector_math/vector_math_64.dart' show Vector3;

import 'colors.dart';
import 'constants.dart';
import 'debug.dart';
import 'ink_well.dart';
import 'material.dart';
import 'material_localizations.dart';
Expand Down Expand Up @@ -495,6 +496,7 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
@override
Widget build(BuildContext context) {
assert(debugCheckHasDirectionality(context));
assert(debugCheckHasMaterialLocalizations(context));

// Labels apply up to _bottomMargin padding. Remainder is media padding.
final double additionalBottomPadding = math.max(MediaQuery.of(context).padding.bottom - _kBottomMargin, 0.0);
Expand Down
2 changes: 2 additions & 0 deletions packages/flutter/lib/src/material/bottom_sheet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';

import 'colors.dart';
import 'debug.dart';
import 'material.dart';
import 'material_localizations.dart';
import 'scaffold.dart';
Expand Down Expand Up @@ -315,6 +316,7 @@ Future<T> showModalBottomSheet<T>({
}) {
assert(context != null);
assert(builder != null);
assert(debugCheckHasMaterialLocalizations(context));
return Navigator.push(context, _ModalBottomSheetRoute<T>(
builder: builder,
theme: Theme.of(context, shadowThemeOnly: true),
Expand Down
1 change: 1 addition & 0 deletions packages/flutter/lib/src/material/chip.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1429,6 +1429,7 @@ class _RawChipState extends State<RawChip> with TickerProviderStateMixin<RawChip
assert(debugCheckHasMaterial(context));
assert(debugCheckHasMediaQuery(context));
assert(debugCheckHasDirectionality(context));
assert(debugCheckHasMaterialLocalizations(context));

final ThemeData theme = Theme.of(context);
final ChipThemeData chipTheme = ChipTheme.of(context);
Expand Down
1 change: 1 addition & 0 deletions packages/flutter/lib/src/material/date_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,7 @@ class _DatePickerDialogState extends State<_DatePickerDialog> {

@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context));
final ThemeData theme = Theme.of(context);
final Widget picker = Flexible(
child: SizedBox(
Expand Down
61 changes: 61 additions & 0 deletions packages/flutter/lib/src/material/debug.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import 'package:flutter/widgets.dart';

import 'material.dart';
import 'material_localizations.dart';

/// Asserts that the given context has a [Material] ancestor.
///
Expand Down Expand Up @@ -66,3 +67,63 @@ bool debugCheckHasMaterial(BuildContext context) {
}());
return true;
}


/// Asserts that the given context has a [Localizations] ancestor that contains
/// a [MaterialLocalizations] delegate.
///
/// Used by many material design widgets to make sure that they are
/// only used in contexts where they have access to localizations.
///
/// To call this function, use the following pattern, typically in the
/// relevant Widget's build method:
///
/// ```dart
/// assert(debugCheckHasMaterialLocalizations(context));
/// ```
///
/// Does nothing if asserts are disabled. Always returns true.
bool debugCheckHasMaterialLocalizations(BuildContext context) {
assert(() {
if (Localizations.of<MaterialLocalizations>(context, MaterialLocalizations) == null) {
final StringBuffer message = StringBuffer();
message.writeln('No MaterialLocalizations found.');
message.writeln(
'${context.widget.runtimeType} widgets require MaterialLocalizations '
'to be provided by a Localizations widget ancestor.'
);
message.writeln(
'Localizations are used to generate many different messages, labels,'
'and abbreviations which are used by the material library. '
);
message.writeln(
'To introduce a MaterialLocalizations, either use a '
' MaterialApp at the root of your application to include them '
'automatically, or add a Localization widget with a '
'MaterialLocalizations delegate.'
);
message.writeln(
'The specific widget that could not find a MaterialLocalizations ancestor was:'
);
message.writeln(' ${context.widget}');
final List<Widget> ancestors = <Widget>[];
context.visitAncestorElements((Element element) {
ancestors.add(element.widget);
return true;
});
if (ancestors.isNotEmpty) {
message.write('The ancestors of this widget were:');
for (Widget ancestor in ancestors)
message.write('\n $ancestor');
} else {
message.writeln(
'This widget is the root of the tree, so it has no '
'ancestors, let alone a "Localizations" ancestor.'
);
}
throw FlutterError(message.toString());
}
return true;
}());
return true;
}
4 changes: 4 additions & 0 deletions packages/flutter/lib/src/material/dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:flutter/widgets.dart';
import 'button_bar.dart';
import 'button_theme.dart';
import 'colors.dart';
import 'debug.dart';
import 'ink_well.dart';
import 'material.dart';
import 'material_localizations.dart';
Expand Down Expand Up @@ -232,6 +233,7 @@ class AlertDialog extends StatelessWidget {

@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context));
final List<Widget> children = <Widget>[];
String label = semanticLabel;

Expand Down Expand Up @@ -490,6 +492,7 @@ class SimpleDialog extends StatelessWidget {

@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context));
final List<Widget> body = <Widget>[];
String label = semanticLabel;

Expand Down Expand Up @@ -596,6 +599,7 @@ Future<T> showDialog<T>({
WidgetBuilder builder,
}) {
assert(child == null || builder == null);
assert(debugCheckHasMaterialLocalizations(context));
return showGeneralDialog(
context: context,
pageBuilder: (BuildContext buildContext, Animation<double> animation, Animation<double> secondaryAnimation) {
Expand Down
3 changes: 3 additions & 0 deletions packages/flutter/lib/src/material/drawer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';

import 'colors.dart';
import 'debug.dart';
import 'list_tile.dart';
import 'material.dart';
import 'material_localizations.dart';
Expand Down Expand Up @@ -116,6 +117,7 @@ class Drawer extends StatelessWidget {

@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context));
String label = semanticLabel;
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
Expand Down Expand Up @@ -431,6 +433,7 @@ class DrawerControllerState extends State<DrawerController> with SingleTickerPro
}
@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context));
return ListTileTheme(
style: ListTileStyle.drawer,
child: _buildDrawer(context),
Expand Down
2 changes: 2 additions & 0 deletions packages/flutter/lib/src/material/dropdown.dart
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> {
//
// 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 _DropdownRoute<T> route = widget.route;
final double unit = 0.5 / (route.items.length + 1.5);
Expand Down Expand Up @@ -631,6 +632,7 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi
@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context));
assert(debugCheckHasMaterialLocalizations(context));

// The width of the button and the menu are defined by the widest
// item and the width of the hint.
Expand Down
1 change: 1 addition & 0 deletions packages/flutter/lib/src/material/expand_icon.dart
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ class _ExpandIconState extends State<ExpandIcon> with SingleTickerProviderStateM
@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context));
assert(debugCheckHasMaterialLocalizations(context));
final MaterialLocalizations localizations = MaterialLocalizations.of(context);
final ThemeData theme = Theme.of(context);
final String onTapHint = widget.isExpanded ? localizations.expandedIconTapHint : localizations.collapsedIconTapHint;
Expand Down
2 changes: 2 additions & 0 deletions packages/flutter/lib/src/material/paginated_data_table.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import 'button_theme.dart';
import 'card.dart';
import 'data_table.dart';
import 'data_table_source.dart';
import 'debug.dart';
import 'dropdown.dart';
import 'icon_button.dart';
import 'icons.dart';
Expand Down Expand Up @@ -286,6 +287,7 @@ class PaginatedDataTableState extends State<PaginatedDataTable> {
@override
Widget build(BuildContext context) {
// 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);
// HEADER
Expand Down
3 changes: 3 additions & 0 deletions packages/flutter/lib/src/material/popup_menu.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';

import 'constants.dart';
import 'debug.dart';
import 'divider.dart';
import 'icon_button.dart';
import 'icons.dart';
Expand Down Expand Up @@ -713,6 +714,7 @@ Future<T> showMenu<T>({
}) {
assert(context != null);
assert(items != null && items.isNotEmpty);
assert(debugCheckHasMaterialLocalizations(context));
String label = semanticLabel;
switch (defaultTargetPlatform) {
case TargetPlatform.iOS:
Expand Down Expand Up @@ -911,6 +913,7 @@ class _PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {

@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context));
return widget.child != null
? InkWell(
onTap: showButtonMenu,
Expand Down
2 changes: 2 additions & 0 deletions packages/flutter/lib/src/material/reorderable_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'dart:math';
import 'package:flutter/widgets.dart';
import 'package:flutter/rendering.dart';

import 'debug.dart';
import 'material.dart';
import 'material_localizations.dart';

Expand Down Expand Up @@ -510,6 +511,7 @@ class _ReorderableListContentState extends State<_ReorderableListContent> with T

@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context));
// We use the layout builder to constrain the cross-axis size of dragging child widgets.
return LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) {
final List<Widget> wrappedChildren = <Widget>[];
Expand Down
2 changes: 2 additions & 0 deletions packages/flutter/lib/src/material/search.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:flutter/widgets.dart';

import 'app_bar.dart';
import 'colors.dart';
import 'debug.dart';
import 'input_border.dart';
import 'input_decorator.dart';
import 'material_localizations.dart';
Expand Down Expand Up @@ -389,6 +390,7 @@ class _SearchPageState<T> extends State<_SearchPage<T>> {

@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context));
final ThemeData theme = widget.delegate.appBarTheme(context);
final String searchFieldLabel = MaterialLocalizations.of(context).searchFieldLabel;
Widget body;
Expand Down
2 changes: 2 additions & 0 deletions packages/flutter/lib/src/material/text_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,8 @@ class _TextFieldState extends State<TextField> with AutomaticKeepAliveClientMixi
Widget build(BuildContext context) {
super.build(context); // See AutomaticKeepAliveClientMixin.
assert(debugCheckHasMaterial(context));
// TODO(jonahwilliams): uncomment out this check once we have migrated tests.
// assert(debugCheckHasMaterialLocalizations(context));
assert(debugCheckHasDirectionality(context));
final ThemeData themeData = Theme.of(context);
final TextStyle style = widget.style ?? themeData.textTheme.subhead;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class _AccountDetails extends StatelessWidget {
@override
Widget build(BuildContext context) {
assert(debugCheckHasDirectionality(context));
assert(debugCheckHasMaterialLocalizations(context));

final ThemeData theme = Theme.of(context);
final List<Widget> children = <Widget>[];
Expand Down Expand Up @@ -308,6 +309,7 @@ class _UserAccountsDrawerHeaderState extends State<UserAccountsDrawerHeader> {
@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context));
assert(debugCheckHasMaterialLocalizations(context));
return Semantics(
container: true,
label: MaterialLocalizations.of(context).signedInLabel,
Expand Down
Loading

0 comments on commit 9bf8502

Please sign in to comment.