debug.dart 7.21 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5 6 7
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/widgets.dart';

import 'material.dart';
8
import 'material_localizations.dart';
9
import 'scaffold.dart' show Scaffold, ScaffoldMessenger;
10

11 12 13
// Examples can assume:
// late BuildContext context;

14 15
/// Asserts that the given context has a [Material] ancestor within the closest
/// [LookupBoundary].
16
///
17
/// Used by many Material Design widgets to make sure that they are
18 19
/// only used in contexts where they can print ink onto some material.
///
20
/// To call this function, use the following pattern, typically in the
21
/// relevant Widget's build method:
22 23 24 25 26
///
/// ```dart
/// assert(debugCheckHasMaterial(context));
/// ```
///
27 28 29 30
/// Always place this before any early returns, so that the invariant is checked
/// in all cases. This prevents bugs from hiding until a particular codepath is
/// hit.
///
31 32
/// This method can be expensive (it walks the element tree).
///
33
/// Does nothing if asserts are disabled. Always returns true.
34 35
bool debugCheckHasMaterial(BuildContext context) {
  assert(() {
36 37
    if (LookupBoundary.findAncestorWidgetOfExactType<Material>(context) == null) {
      final bool hiddenByBoundary = LookupBoundary.debugIsHidingAncestorWidgetOfExactType<Material>(context);
38
      throw FlutterError.fromParts(<DiagnosticsNode>[
39 40 41 42 43
        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.'
          ),
44 45
        ErrorDescription(
          '${context.widget.runtimeType} widgets require a Material '
46
          'widget ancestor within the closest LookupBoundary.\n'
47
          'In Material Design, most widgets are conceptually "printed" on '
48
          "a sheet of material. In Flutter's material library, that "
49 50 51
          'material is represented by the Material widget. It is the '
          'Material widget that renders ink splashes, for instance. '
          'Because of this, many material library widgets require that '
52
          'there be a Material widget in the tree above them.',
53 54 55 56 57 58
        ),
        ErrorHint(
          'To introduce a Material widget, you can either directly '
          'include one, or use a widget that contains Material itself, '
          'such as a Card, Dialog, Drawer, or Scaffold.',
        ),
59 60
        ...context.describeMissingAncestor(expectedAncestorType: Material),
      ]);
Hixie's avatar
Hixie committed
61 62
    }
    return true;
63
  }());
64 65
  return true;
}
66 67 68 69

/// Asserts that the given context has a [Localizations] ancestor that contains
/// a [MaterialLocalizations] delegate.
///
70
/// Used by many Material Design widgets to make sure that they are
71 72 73 74 75 76 77 78 79
/// 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));
/// ```
///
80 81 82 83
/// Always place this before any early returns, so that the invariant is checked
/// in all cases. This prevents bugs from hiding until a particular codepath is
/// hit.
///
84 85 86 87 88
/// This function has the side-effect of establishing an inheritance
/// relationship with the nearest [Localizations] widget (see
/// [BuildContext.dependOnInheritedWidgetOfExactType]). This is ok if the caller
/// always also calls [Localizations.of] or [Localizations.localeOf].
///
89 90 91 92
/// Does nothing if asserts are disabled. Always returns true.
bool debugCheckHasMaterialLocalizations(BuildContext context) {
  assert(() {
    if (Localizations.of<MaterialLocalizations>(context, MaterialLocalizations) == null) {
93 94 95 96
      throw FlutterError.fromParts(<DiagnosticsNode>[
        ErrorSummary('No MaterialLocalizations found.'),
        ErrorDescription(
          '${context.widget.runtimeType} widgets require MaterialLocalizations '
97
          'to be provided by a Localizations widget ancestor.',
98 99
        ),
        ErrorDescription(
100
          'The material library uses Localizations to generate messages, '
101
          'labels, and abbreviations.',
102 103 104 105 106
        ),
        ErrorHint(
          '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 '
107
          'MaterialLocalizations delegate.',
108
        ),
109
        ...context.describeMissingAncestor(expectedAncestorType: MaterialLocalizations),
110
      ]);
111 112 113 114 115
    }
    return true;
  }());
  return true;
}
116 117 118 119 120 121 122 123 124 125 126 127 128

/// Asserts that the given context has a [Scaffold] ancestor.
///
/// Used by various widgets to make sure that they are only used in an
/// appropriate context.
///
/// To invoke this function, use the following pattern, typically in the
/// relevant Widget's build method:
///
/// ```dart
/// assert(debugCheckHasScaffold(context));
/// ```
///
129 130 131 132
/// Always place this before any early returns, so that the invariant is checked
/// in all cases. This prevents bugs from hiding until a particular codepath is
/// hit.
///
133 134
/// This method can be expensive (it walks the element tree).
///
135 136 137
/// Does nothing if asserts are disabled. Always returns true.
bool debugCheckHasScaffold(BuildContext context) {
  assert(() {
138
    if (context.widget is! Scaffold && context.findAncestorWidgetOfExactType<Scaffold>() == null) {
139 140 141 142 143
      throw FlutterError.fromParts(<DiagnosticsNode>[
        ErrorSummary('No Scaffold widget found.'),
        ErrorDescription('${context.widget.runtimeType} widgets require a Scaffold widget ancestor.'),
        ...context.describeMissingAncestor(expectedAncestorType: Scaffold),
        ErrorHint(
144
          'Typically, the Scaffold widget is introduced by the MaterialApp or '
145 146
          'WidgetsApp widget at the top of your application widget tree.',
        ),
147
      ]);
148 149 150 151 152
    }
    return true;
  }());
  return true;
}
153 154 155 156 157 158 159 160 161 162 163 164 165

/// Asserts that the given context has a [ScaffoldMessenger] ancestor.
///
/// Used by various widgets to make sure that they are only used in an
/// appropriate context.
///
/// To invoke this function, use the following pattern, typically in the
/// relevant Widget's build method:
///
/// ```dart
/// assert(debugCheckHasScaffoldMessenger(context));
/// ```
///
166 167 168 169
/// Always place this before any early returns, so that the invariant is checked
/// in all cases. This prevents bugs from hiding until a particular codepath is
/// hit.
///
170 171
/// This method can be expensive (it walks the element tree).
///
172 173 174 175 176 177 178 179 180 181
/// Does nothing if asserts are disabled. Always returns true.
bool debugCheckHasScaffoldMessenger(BuildContext context) {
  assert(() {
    if (context.findAncestorWidgetOfExactType<ScaffoldMessenger>() == null) {
      throw FlutterError.fromParts(<DiagnosticsNode>[
        ErrorSummary('No ScaffoldMessenger widget found.'),
        ErrorDescription('${context.widget.runtimeType} widgets require a ScaffoldMessenger widget ancestor.'),
        ...context.describeMissingAncestor(expectedAncestorType: ScaffoldMessenger),
        ErrorHint(
          'Typically, the ScaffoldMessenger widget is introduced by the MaterialApp '
182 183
          'at the top of your application widget tree.',
        ),
184 185 186 187 188 189
      ]);
    }
    return true;
  }());
  return true;
}