debug.dart 5.87 KB
Newer Older
1 2 3 4 5 6 7
// Copyright 2015 The Chromium 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/widgets.dart';

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

11
/// Asserts that the given context has a [Material] ancestor.
12
///
13 14 15
/// Used by many material design widgets to make sure that they are
/// only used in contexts where they can print ink onto some material.
///
16
/// To call this function, use the following pattern, typically in the
17
/// relevant Widget's build method:
18 19 20 21 22 23
///
/// ```dart
/// assert(debugCheckHasMaterial(context));
/// ```
///
/// Does nothing if asserts are disabled. Always returns true.
24 25
bool debugCheckHasMaterial(BuildContext context) {
  assert(() {
Hixie's avatar
Hixie committed
26
    if (context.widget is! Material && context.ancestorWidgetOfExactType(Material) == null) {
27
      final StringBuffer message = StringBuffer();
28 29 30 31
      message.writeln('No Material widget found.');
      message.writeln(
        '${context.widget.runtimeType} widgets require a Material '
        'widget ancestor.'
Hixie's avatar
Hixie committed
32
      );
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
      message.writeln(
        '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 '
        'Material widget that renders ink splashes, for instance. '
        'Because of this, many material library widgets require that '
        'there be a Material widget in the tree above them.'
      );
      message.writeln(
        '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.'
      );
      message.writeln(
        'The specific widget that could not find a Material 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 "Material" ancestor.'
        );
      }
65
      throw FlutterError(message.toString());
Hixie's avatar
Hixie committed
66 67
    }
    return true;
68
  }());
69 70
  return true;
}
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130


/// 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;
}
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163

/// 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));
/// ```
///
/// Does nothing if asserts are disabled. Always returns true.
bool debugCheckHasScaffold(BuildContext context) {
  assert(() {
    if (context.widget is! Scaffold && context.ancestorWidgetOfExactType(Scaffold) == null) {
      final Element element = context;
      throw FlutterError(
          'No Scaffold widget found.\n'
          '${context.widget.runtimeType} widgets require a Scaffold widget ancestor.\n'
          'The Specific widget that could not find a Scaffold ancestor was:\n'
          '  ${context.widget}\n'
          'The ownership chain for the affected widget is:\n'
          '  ${element.debugGetCreatorChain(10)}\n'
          'Typically, the Scaffold widget is introduced by the MaterialApp or '
          'WidgetsApp widget at the top of your application widget tree.'
      );
    }
    return true;
  }());
  return true;
}