Unverified Commit 262f12b4 authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Remove remaining "## Sample code" segments, and fix the snippet generator. (#27793)

This converts all remaining "## Sample code" segments into snippets, and fixes
the snippet generator to handle multiple snippets in the same dartdoc block
properly.

I also generated, compiled, and ran each of the existing application samples,
and fixed them up to be more useful and/or just run without errors.

This PR fixes these problems with examples:

1. Switching tabs in a snippet now works if there is more than one snippet in
   a single dartdoc block.
2. Generation of snippet code now works if there is more than one snippet.
3. Contrast of text and links in the code sample block has been improved to
   recommended levels.
4. Added five new snippet templates, including a "freeform" template to make
   it possible to show examples that need to change the app instantiation.
5. Fixed several examples to run properly, a couple by adding the "Scaffold"
   widget to the template, a couple by just fixing their code.
6. Fixed visual look of some of the samples when they run by placing many
   samples inside of a Scaffold.
7. In order to make it easier to run locally, changed the sample analyzer to
   remove the contents of the supplied temp directory before running, since
   having files that hang around is problematic (only a problem when running
   locally with the `--temp` argument).
8. Added a `SampleCheckerException` class, and handle sample checking
   exceptions more gracefully.
9. Deprecated the old "## Sample code" designation, and added enforcement for
   the deprecation.
10. Removed unnecessary `new` from templates (although they never appeared in
   the samples thanks to dartfmt, but still).

Fixes #26398
Fixes #27411
parent 7862bef1
This diff is collapsed.
...@@ -18,8 +18,7 @@ ...@@ -18,8 +18,7 @@
/// blabla 0.0, the penzance blabla is blabla not blabla at all. Bla the blabla /// blabla 0.0, the penzance blabla is blabla not blabla at all. Bla the blabla
/// 1.0, the blabla is blabla blabla blabla an blabla blabla. /// 1.0, the blabla is blabla blabla blabla an blabla blabla.
/// ///
/// ### Sample code /// {@tool sample}
///
/// Bla blabla blabla some [Text] when the `_blabla` blabla blabla is true, and /// Bla blabla blabla some [Text] when the `_blabla` blabla blabla is true, and
/// blabla it when it is blabla: /// blabla it when it is blabla:
/// ///
...@@ -29,9 +28,9 @@ ...@@ -29,9 +28,9 @@
/// child: const Text('Poor wandering ones!'), /// child: const Text('Poor wandering ones!'),
/// ) /// )
/// ``` /// ```
/// {@end-tool}
/// ///
/// ## Sample code /// {@tool sample}
///
/// Bla blabla blabla some [Text] when the `_blabla` blabla blabla is true, and /// Bla blabla blabla some [Text] when the `_blabla` blabla blabla is true, and
/// blabla finale blabla: /// blabla finale blabla:
/// ///
...@@ -41,3 +40,4 @@ ...@@ -41,3 +40,4 @@
/// child: const Text('Poor wandering ones!'), /// child: const Text('Poor wandering ones!'),
/// ) /// )
/// ``` /// ```
/// {@end-tool}
...@@ -17,9 +17,9 @@ void main() { ...@@ -17,9 +17,9 @@ void main() {
..removeWhere((String line) => line.startsWith('Analyzer output:')); ..removeWhere((String line) => line.startsWith('Analyzer output:'));
expect(process.exitCode, isNot(equals(0))); expect(process.exitCode, isNot(equals(0)));
expect(stderrLines, <String>[ expect(stderrLines, <String>[
'known_broken_documentation.dart:27:9: new Opacity(', 'known_broken_documentation.dart:26:9: new Opacity(',
'>>> Unnecessary new keyword (unnecessary_new)', '>>> Unnecessary new keyword (unnecessary_new)',
'known_broken_documentation.dart:39:9: new Opacity(', 'known_broken_documentation.dart:38:9: new Opacity(',
'>>> Unnecessary new keyword (unnecessary_new)', '>>> Unnecessary new keyword (unnecessary_new)',
'', '',
'Found 1 sample code errors.', 'Found 1 sample code errors.',
......
/* Styles for handling custom code snippets */ /* Styles for handling custom code snippets */
.snippet-container { .snippet-container {
background-color: #45aae8; background-color: #2372a3;
padding: 10px; padding: 10px;
overflow: auto; overflow: auto;
} }
...@@ -30,8 +30,21 @@ ...@@ -30,8 +30,21 @@
color: white; color: white;
} }
.snippet-description a:link {
color: #c7fcf4;
}
.snippet-description a:visited {
color: #c7dbfc;
}
.snippet-description a:hover {
color: white;
}
.snippet-description a:active {
color: #80b0fc;
}
.snippet-buttons button { .snippet-buttons button {
background-color: #45aae8; background-color: #2372a3;
border-style: none; border-style: none;
color: white; color: white;
padding: 10px 24px; padding: 10px 24px;
...@@ -82,7 +95,7 @@ ...@@ -82,7 +95,7 @@
height: 28px; height: 28px;
width: 28px; width: 28px;
transition: .3s ease; transition: .3s ease;
background-color: #45aae8; background-color: #2372a3;
} }
.copy-button { .copy-button {
...@@ -102,7 +115,7 @@ ...@@ -102,7 +115,7 @@
.copy-image { .copy-image {
opacity: 0.65; opacity: 0.65;
color: #45aae8; color: #2372a3;
font-size: 28px; font-size: 28px;
padding-top: 4px; padding-top: 4px;
} }
...@@ -2,15 +2,10 @@ ...@@ -2,15 +2,10 @@
* Scripting for handling custom code snippets * Scripting for handling custom code snippets
*/ */
const shortSnippet = 'shortSnippet';
const longSnippet = 'longSnippet';
var visibleSnippet = shortSnippet;
/** /**
* Shows the requested snippet. Values for "name" can be "shortSnippet" or * Shows the requested snippet, and stores the current state in visibleSnippet.
* "longSnippet".
*/ */
function showSnippet(name) { function showSnippet(name, visibleSnippet) {
if (visibleSnippet == name) return; if (visibleSnippet == name) return;
if (visibleSnippet != null) { if (visibleSnippet != null) {
var shown = document.getElementById(visibleSnippet); var shown = document.getElementById(visibleSnippet);
...@@ -39,6 +34,7 @@ function showSnippet(name) { ...@@ -39,6 +34,7 @@ function showSnippet(name) {
if (button != null) { if (button != null) {
button.setAttributeNode(selectedAttribute); button.setAttributeNode(selectedAttribute);
} }
return visibleSnippet;
} }
// Finds a sibling to given element with the given id. // Finds a sibling to given element with the given id.
...@@ -64,8 +60,8 @@ function supportsCopying() { ...@@ -64,8 +60,8 @@ function supportsCopying() {
// Copies the text inside the currently visible snippet to the clipboard, or the // Copies the text inside the currently visible snippet to the clipboard, or the
// given element, if any. // given element, if any.
function copyTextToClipboard(element) { function copyTextToClipboard(element) {
if (element == null) { if (typeof element === 'string') {
var elementSelector = '#' + visibleSnippet + ' .language-dart'; var elementSelector = '#' + element + ' .language-dart';
element = document.querySelector(elementSelector); element = document.querySelector(elementSelector);
if (element == null) { if (element == null) {
console.log( console.log(
......
...@@ -7,8 +7,9 @@ snippets. ...@@ -7,8 +7,9 @@ snippets.
This takes code in dartdocs, like this: This takes code in dartdocs, like this:
```dart ```dart
/// The following is a skeleton of a stateless widget subclass called `GreenFrog`:
/// {@tool snippet --template="stateless_widget"} /// {@tool snippet --template="stateless_widget"}
/// The following is a skeleton of a stateless widget subclass called `GreenFrog`.
/// ```dart
/// class GreenFrog extends StatelessWidget { /// class GreenFrog extends StatelessWidget {
/// const GreenFrog({ Key key }) : super(key: key); /// const GreenFrog({ Key key }) : super(key: key);
/// ///
...@@ -17,6 +18,7 @@ This takes code in dartdocs, like this: ...@@ -17,6 +18,7 @@ This takes code in dartdocs, like this:
/// return Container(color: const Color(0xFF2DBD3A)); /// return Container(color: const Color(0xFF2DBD3A));
/// } /// }
/// } /// }
/// ```
/// {@end-tool} /// {@end-tool}
``` ```
......
{@inject-html} {@inject-html}
<div class="snippet-buttons"> <div class="snippet-buttons">
<button id="shortSnippetButton" onclick="showSnippet(shortSnippet);" selected>Sample</button> <script>var visibleSnippet{{serial}} = "shortSnippet{{serial}}";</script>
<button id="longSnippetButton" onclick="showSnippet(longSnippet);">Sample in an App</button> <button id="shortSnippet{{serial}}Button"
onclick="visibleSnippet{{serial}} = showSnippet('shortSnippet{{serial}}', visibleSnippet{{serial}});"
selected>Sample</button>
<button id="longSnippet{{serial}}Button"
onclick="visibleSnippet{{serial}} = showSnippet('longSnippet{{serial}}', visibleSnippet{{serial}});">Sample in an App</button>
</div> </div>
<div class="snippet-container"> <div class="snippet-container">
<div class="snippet" id="shortSnippet"> <div class="snippet" id="shortSnippet{{serial}}">
{{description}} {{description}}
<div class="copyable-container"> <div class="copyable-container">
<button class="copy-button-overlay copy-button" title="Copy to clipboard" <button class="copy-button-overlay copy-button" title="Copy to clipboard"
onclick="copyTextToClipboard();"> onclick="copyTextToClipboard(visibleSnippet{{serial}});">
<i class="material-icons copy-image">assignment</i> <i class="material-icons copy-image">assignment</i>
</button> </button>
<pre class="language-{{language}}"><code class="language-{{language}}">{{code}}</code></pre> <pre class="language-{{language}}"><code class="language-{{language}}">{{code}}</code></pre>
</div> </div>
</div> </div>
<div class="snippet" id="longSnippet" hidden> <div class="snippet" id="longSnippet{{serial}}" hidden>
<div class="snippet-description">To create a sample project with this code snippet, run:<br/> <div class="snippet-description">To create a sample project with this code snippet, run:<br/>
<span class="snippet-create-command">flutter create --sample={{id}} mysample</span> <span class="snippet-create-command">flutter create --sample={{id}} mysample</span>
</div> </div>
<div class="copyable-container"> <div class="copyable-container">
<button class="copy-button-overlay copy-button" title="Copy to clipboard" <button class="copy-button-overlay copy-button" title="Copy to clipboard"
onclick="copyTextToClipboard();"> onclick="copyTextToClipboard(visibleSnippet{{serial}});">
<i class="material-icons copy-image">assignment</i> <i class="material-icons copy-image">assignment</i>
</button> </button>
<pre class="language-{{language}}"><code class="language-{{language}}">{{app}}</code></pre> <pre class="language-{{language}}"><code class="language-{{language}}">{{app}}</code></pre>
......
{@inject-html} {@inject-html}
<div class="snippet-buttons"> <div class="snippet-buttons">
<button id="shortSnippetButton" selected>Sample</button> <button id="shortSnippet{{serial}}Button" selected>Sample</button>
</div> </div>
<div class="snippet-container"> <div class="snippet-container">
<div class="snippet">{{description}} <div class="snippet">{{description}}
......
...@@ -49,6 +49,11 @@ additional burden, since all code will also be compiled to be sure it compiles). ...@@ -49,6 +49,11 @@ additional burden, since all code will also be compiled to be sure it compiles).
The templates available for using as an argument to the snippets tool are as The templates available for using as an argument to the snippets tool are as
follows: follows:
- [`freeform`](freeform.tmpl) :
This is a simple template for which you provide everything. It has no code of
its own, just the sections for `imports`, `main`, and `preamble`. You must
provide the `main` section in order to have a `main()`.
- [`stateful_widget`](stateful_widget.tmpl) : - [`stateful_widget`](stateful_widget.tmpl) :
The default code block will be placed as the body of the `State` object of a The default code block will be placed as the body of the `State` object of a
StatefulWidget subclass. Because the default code block is placed as the body StatefulWidget subclass. Because the default code block is placed as the body
...@@ -58,8 +63,26 @@ follows: ...@@ -58,8 +63,26 @@ follows:
function calls are not allowed in the preamble. It also has an `imports` function calls are not allowed in the preamble. It also has an `imports`
section to import additional packages. Please only import things that are part section to import additional packages. Please only import things that are part
of flutter or part of default dependencies for a `flutter create` project. of flutter or part of default dependencies for a `flutter create` project.
It creates a WidgetsApp around the child stateful widget.
- [`stateless_widget`](stateless_widget.tmpl) : - [`stateless_widget`](stateless_widget.tmpl) :
Identical to the `stateful_widget` template, except that the default code Identical to the `stateful_widget` template, except that the default code
block is inserted as the return value for a pre-existing `build` function in a block is inserted as the `build` function in a
StatelessWidget, instead of being at the class level. StatelessWidget. There is no need to include the @override before the build
funciton (the template adds this for you).
- [`stateful_widget_material`](stateful_widget_material.tmpl) : Similar to
`stateful_widget`, except that it imports the material library, and uses
a MaterialApp instead of WidgetsApp.
- [`stateless_widget_material`](stateless_widget_material.tmpl) : Similar to
`stateless_widget`, except that it imports the material library, and uses
a MaterialApp instead of WidgetsApp.
- [`stateful_widget_scaffold`](stateful_widget_scaffold.tmpl) : Similar to
`stateful_widget_material`, except that it wraps the stateful widget with a
Scaffold.
- [`stateless_widget_scaffold`](stateless_widget_scaffold.tmpl) : Similar to
`stateless_widget_material`, except that it wraps the stateless widget with a
Scaffold.
// Flutter code sample for {{id}}
{{description}}
{{code-imports}}
{{code-main}}
{{code-preamble}}
{{code}}
// Flutter code sample for {{id}}
{{description}} {{description}}
import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart';
{{code-imports}} {{code-imports}}
void main() => runApp(new MyApp()); void main() => runApp(new MyApp());
/// This Widget is the main application widget.
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new MaterialApp( return WidgetsApp(
title: 'Flutter Code Sample for {{id}}', title: 'Flutter Code Sample',
theme: new ThemeData( home: MyStatefulWidget(),
primarySwatch: Colors.blue, color: const Color(0xffffffff),
),
home: new MyStatefulWidget(),
); );
} }
} }
{{code-preamble}} {{code-preamble}}
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget { class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key); MyStatefulWidget({Key key}) : super(key: key);
@override @override
_MyStatefulWidgetState createState() => new _MyStatefulWidgetState(); _MyStatefulWidgetState createState() => _MyStatefulWidgetState();
} }
// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> { class _MyStatefulWidgetState extends State<MyStatefulWidget> {
{{code}} {{code}}
} }
// Flutter code sample for {{id}}
{{description}}
import 'package:flutter/material.dart';
{{code-imports}}
void main() => runApp(new MyApp());
/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
{{code-preamble}}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
@override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
{{code}}
}
// Flutter code sample for {{id}}
{{description}}
import 'package:flutter/material.dart';
{{code-imports}}
void main() => runApp(new MyApp());
/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: Text(_title)),
body: MyStatefulWidget(),
),
);
}
}
{{code-preamble}}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
@override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
{{code}}
}
// Flutter code sample for {{id}}
{{description}} {{description}}
import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart';
{{code-imports}} {{code-imports}}
void main() => runApp(new MyApp()); void main() => runApp(new MyApp());
/// This Widget is the main application widget.
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new MaterialApp( return WidgetsApp(
title: 'Flutter Code Sample for {{id}}', title: 'Flutter Code Sample',
theme: new ThemeData( builder: (BuildContext context, Widget navigator) {
primarySwatch: Colors.blue, return MyStatelessWidget();
), },
home: new MyStatelessWidget(), color: const Color(0xffffffff),
); );
} }
} }
{{code-preamble}} {{code-preamble}}
/// This is the stateless widget that the main application instantiates.
class MyStatelessWidget extends StatelessWidget { class MyStatelessWidget extends StatelessWidget {
MyStatelessWidget({Key key}) : super(key: key); MyStatelessWidget({Key key}) : super(key: key);
@override @override
Widget build(BuildContext context) { {{code}}
return {{code}};
}
} }
// Flutter code sample for {{id}}
{{description}}
import 'package:flutter/material.dart';
{{code-imports}}
void main() => runApp(new MyApp());
/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: MyStatelessWidget(),
);
}
}
{{code-preamble}}
/// This is the stateless widget that the main application instantiates.
class MyStatelessWidget extends StatelessWidget {
MyStatelessWidget({Key key}) : super(key: key);
@override
{{code}}
}
// Flutter code sample for {{id}}
{{description}}
import 'package:flutter/material.dart';
{{code-imports}}
void main() => runApp(new MyApp());
/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: Text(_title)),
body: MyStatelessWidget(),
),
);
}
}
{{code-preamble}}
/// This is the stateless widget that the main application instantiates.
class MyStatelessWidget extends StatelessWidget {
MyStatelessWidget({Key key}) : super(key: key);
@override
{{code}}
}
...@@ -11,6 +11,7 @@ import 'package:platform/platform.dart'; ...@@ -11,6 +11,7 @@ import 'package:platform/platform.dart';
import 'configuration.dart'; import 'configuration.dart';
import 'snippets.dart'; import 'snippets.dart';
const String _kSerialOption = 'serial';
const String _kElementOption = 'element'; const String _kElementOption = 'element';
const String _kHelpOption = 'help'; const String _kHelpOption = 'help';
const String _kInputOption = 'input'; const String _kInputOption = 'input';
...@@ -75,6 +76,11 @@ void main(List<String> argList) { ...@@ -75,6 +76,11 @@ void main(List<String> argList) {
defaultsTo: environment['ELEMENT_NAME'], defaultsTo: environment['ELEMENT_NAME'],
help: 'The name of the element that this snippet belongs to.', help: 'The name of the element that this snippet belongs to.',
); );
parser.addOption(
_kSerialOption,
defaultsTo: environment['INVOCATION_INDEX'],
help: 'A unique serial number for this snippet tool invocation.',
);
parser.addFlag( parser.addFlag(
_kHelpOption, _kHelpOption,
defaultsTo: false, defaultsTo: false,
...@@ -117,6 +123,7 @@ void main(List<String> argList) { ...@@ -117,6 +123,7 @@ void main(List<String> argList) {
final String packageName = args[_kPackageOption] != null && args[_kPackageOption].isNotEmpty ? args[_kPackageOption] : null; final String packageName = args[_kPackageOption] != null && args[_kPackageOption].isNotEmpty ? args[_kPackageOption] : null;
final String libraryName = args[_kLibraryOption] != null && args[_kLibraryOption].isNotEmpty ? args[_kLibraryOption] : null; final String libraryName = args[_kLibraryOption] != null && args[_kLibraryOption].isNotEmpty ? args[_kLibraryOption] : null;
final String elementName = args[_kElementOption] != null && args[_kElementOption].isNotEmpty ? args[_kElementOption] : null; final String elementName = args[_kElementOption] != null && args[_kElementOption].isNotEmpty ? args[_kElementOption] : null;
final String serial = args[_kSerialOption] != null && args[_kSerialOption].isNotEmpty ? args[_kSerialOption] : null;
final List<String> id = <String>[]; final List<String> id = <String>[];
if (args[_kOutputOption] != null) { if (args[_kOutputOption] != null) {
id.add(path.basename(path.basenameWithoutExtension(args[_kOutputOption]))); id.add(path.basename(path.basenameWithoutExtension(args[_kOutputOption])));
...@@ -130,10 +137,13 @@ void main(List<String> argList) { ...@@ -130,10 +137,13 @@ void main(List<String> argList) {
if (elementName != null) { if (elementName != null) {
id.add(elementName); id.add(elementName);
} }
if (serial != null) {
id.add(serial);
}
if (id.isEmpty) { if (id.isEmpty) {
errorExit('Unable to determine ID. At least one of --$_kPackageOption, ' errorExit('Unable to determine ID. At least one of --$_kPackageOption, '
'--$_kLibraryOption, --$_kElementOption, or the environment variables ' '--$_kLibraryOption, --$_kElementOption, -$_kSerialOption, or the environment variables '
'PACKAGE_NAME, LIBRARY_NAME, or ELEMENT_NAME must be non-empty.'); 'PACKAGE_NAME, LIBRARY_NAME, ELEMENT_NAME, or INVOCATION_INDEX must be non-empty.');
} }
} }
...@@ -149,6 +159,7 @@ void main(List<String> argList) { ...@@ -149,6 +159,7 @@ void main(List<String> argList) {
'sourceLine': environment['SOURCE_LINE'] != null 'sourceLine': environment['SOURCE_LINE'] != null
? int.tryParse(environment['SOURCE_LINE']) ? int.tryParse(environment['SOURCE_LINE'])
: null, : null,
'serial': serial,
'package': packageName, 'package': packageName,
'library': libraryName, 'library': libraryName,
'element': elementName, 'element': elementName,
......
...@@ -31,7 +31,8 @@ class SnippetGenerator { ...@@ -31,7 +31,8 @@ class SnippetGenerator {
SnippetGenerator({Configuration configuration}) SnippetGenerator({Configuration configuration})
: configuration = configuration ?? : configuration = configuration ??
// Flutter's root is four directories up from this script. // Flutter's root is four directories up from this script.
Configuration(flutterRoot: Directory(Platform.environment['FLUTTER_ROOT'] ?? path.canonicalize(path.join(path.dirname(path.fromUri(Platform.script)), '..', '..', '..')))) { Configuration(flutterRoot: Directory(Platform.environment['FLUTTER_ROOT']
?? path.canonicalize(path.join(path.dirname(path.fromUri(Platform.script)), '..', '..', '..')))) {
this.configuration.createOutputDirectory(); this.configuration.createOutputDirectory();
} }
...@@ -72,10 +73,10 @@ class SnippetGenerator { ...@@ -72,10 +73,10 @@ class SnippetGenerator {
// Remove any leading/trailing empty comment lines. // Remove any leading/trailing empty comment lines.
// We don't want to remove ALL empty comment lines, only the ones at the // We don't want to remove ALL empty comment lines, only the ones at the
// beginning and the end. // beginning and the end.
while (description.last == '// ') { while (description.isNotEmpty && description.last == '// ') {
description.removeLast(); description.removeLast();
} }
while (description.first == '// ') { while (description.isNotEmpty && description.first == '// ') {
description.removeAt(0); description.removeAt(0);
} }
return description.join('\n').trim(); return description.join('\n').trim();
...@@ -98,7 +99,7 @@ class SnippetGenerator { ...@@ -98,7 +99,7 @@ class SnippetGenerator {
/// ///
/// Takes into account the [type] and doesn't substitute in the id and the app /// Takes into account the [type] and doesn't substitute in the id and the app
/// if not a [SnippetType.application] snippet. /// if not a [SnippetType.application] snippet.
String interpolateSkeleton(SnippetType type, List<_ComponentTuple> injections, String skeleton) { String interpolateSkeleton(SnippetType type, List<_ComponentTuple> injections, String skeleton, Map<String, Object> metadata) {
final List<String> result = <String>[]; final List<String> result = <String>[];
const HtmlEscape htmlEscape = HtmlEscape(); const HtmlEscape htmlEscape = HtmlEscape();
String language; String language;
...@@ -128,12 +129,13 @@ class SnippetGenerator { ...@@ -128,12 +129,13 @@ class SnippetGenerator {
'language': language ?? 'dart', 'language': language ?? 'dart',
}..addAll(type == SnippetType.application }..addAll(type == SnippetType.application
? <String, String>{ ? <String, String>{
'serial': metadata['serial'].toString() ?? '0',
'id': 'id':
injections.firstWhere((_ComponentTuple tuple) => tuple.name == 'id').mergedContent, injections.firstWhere((_ComponentTuple tuple) => tuple.name == 'id').mergedContent,
'app': 'app':
htmlEscape.convert(injections.firstWhere((_ComponentTuple tuple) => tuple.name == 'app').mergedContent), htmlEscape.convert(injections.firstWhere((_ComponentTuple tuple) => tuple.name == 'app').mergedContent),
} }
: <String, String>{'id': '', 'app': ''}); : <String, String>{'serial': '', 'id': '', 'app': ''});
return skeleton.replaceAllMapped(RegExp('{{(${substitutions.keys.join('|')})}}'), (Match match) { return skeleton.replaceAllMapped(RegExp('{{(${substitutions.keys.join('|')})}}'), (Match match) {
return substitutions[match[1]]; return substitutions[match[1]];
}); });
...@@ -180,6 +182,16 @@ class SnippetGenerator { ...@@ -180,6 +182,16 @@ class SnippetGenerator {
return file.readAsStringSync(encoding: Encoding.getByName('utf-8')); return file.readAsStringSync(encoding: Encoding.getByName('utf-8'));
} }
String _addLineNumbers(String app) {
final StringBuffer buffer = StringBuffer();
int count = 0;
for (String line in app.split('\n')) {
count++;
buffer.writeln('${count.toString().padLeft(5, ' ')}: $line');
}
return buffer.toString();
}
/// The main routine for generating snippets. /// The main routine for generating snippets.
/// ///
/// The [input] is the file containing the dartdoc comments (minus the leading /// The [input] is the file containing the dartdoc comments (minus the leading
...@@ -220,7 +232,7 @@ class SnippetGenerator { ...@@ -220,7 +232,7 @@ class SnippetGenerator {
try { try {
app = formatter.format(app); app = formatter.format(app);
} on FormatterException catch (exception) { } on FormatterException catch (exception) {
stderr.write('Code to format:\n$app\n'); stderr.write('Code to format:\n${_addLineNumbers(app)}\n');
errorExit('Unable to format snippet app template: $exception'); errorExit('Unable to format snippet app template: $exception');
} }
...@@ -250,6 +262,6 @@ class SnippetGenerator { ...@@ -250,6 +262,6 @@ class SnippetGenerator {
break; break;
} }
final String skeleton = _loadFileAsUtf8(configuration.getHtmlSkeletonFile(type)); final String skeleton = _loadFileAsUtf8(configuration.getHtmlSkeletonFile(type));
return interpolateSkeleton(type, snippetData, skeleton); return interpolateSkeleton(type, snippetData, skeleton, metadata);
} }
} }
...@@ -32,7 +32,7 @@ import 'package:flutter/foundation.dart'; ...@@ -32,7 +32,7 @@ import 'package:flutter/foundation.dart';
/// look at the physical key to make sure that regardless of the character the /// look at the physical key to make sure that regardless of the character the
/// key produces, you got the key that is in that location on the keyboard. /// key produces, you got the key that is in that location on the keyboard.
/// ///
/// {@tool snippet --template=stateful_widget} /// {@tool snippet --template=stateful_widget_scaffold}
/// This example shows how to detect if the user has selected the logical "Q" /// This example shows how to detect if the user has selected the logical "Q"
/// key. /// key.
/// ///
...@@ -270,7 +270,7 @@ class LogicalKeyboardKey extends Diagnosticable { ...@@ -270,7 +270,7 @@ class LogicalKeyboardKey extends Diagnosticable {
/// looking for "the key next next to the TAB key", since on a French keyboard, /// looking for "the key next next to the TAB key", since on a French keyboard,
/// the key next to the TAB key has an "A" on it. /// the key next to the TAB key has an "A" on it.
/// ///
/// {@tool snippet --template=stateful_widget} /// {@tool snippet --template=stateful_widget_scaffold}
/// This example shows how to detect if the user has selected the physical key /// This example shows how to detect if the user has selected the physical key
/// to the right of the CAPS LOCK key. /// to the right of the CAPS LOCK key.
/// ///
......
...@@ -228,25 +228,28 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget { ...@@ -228,25 +228,28 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget {
/// For less common operations, consider using a [PopupMenuButton] as the /// For less common operations, consider using a [PopupMenuButton] as the
/// last action. /// last action.
/// ///
/// {@tool snippet --template=stateless_widget} /// {@tool snippet --template=stateless_widget_material}
/// ///
/// This sample shows adding an action to an [AppBar] that opens a shopping cart. /// This sample shows adding an action to an [AppBar] that opens a shopping cart.
/// ///
/// ```dart /// ```dart
/// Scaffold( /// Widget build(BuildContext context) {
/// appBar: AppBar( /// return Scaffold(
/// title: Text('Hello World'), /// appBar: AppBar(
/// actions: <Widget>[ /// title: Text('Ready, Set, Shop!'),
/// IconButton( /// actions: <Widget>[
/// icon: Icon(Icons.shopping_cart), /// IconButton(
/// tooltip: 'Open shopping cart', /// icon: Icon(Icons.shopping_cart),
/// onPressed: () { /// tooltip: 'Open shopping cart',
/// // ... /// onPressed: () {
/// }, /// // Implement navigation to shopping cart page here...
/// ), /// print('Shopping cart opened.');
/// ], /// },
/// ), /// ),
/// ) /// ],
/// ),
/// );
/// }
/// ``` /// ```
/// {@end-tool} /// {@end-tool}
final List<Widget> actions; final List<Widget> actions;
......
...@@ -68,8 +68,7 @@ enum BottomNavigationBarType { ...@@ -68,8 +68,7 @@ enum BottomNavigationBarType {
/// case it's assumed that each item will have a different background color /// case it's assumed that each item will have a different background color
/// and that background color will contrast well with white. /// and that background color will contrast well with white.
/// ///
/// ## Sample Code /// {@tool snippet --template=stateful_widget_material}
///
/// This example shows a [BottomNavigationBar] as it is used within a [Scaffold] /// This example shows a [BottomNavigationBar] as it is used within a [Scaffold]
/// widget. The [BottomNavigationBar] has three [BottomNavigationBarItem] /// widget. The [BottomNavigationBar] has three [BottomNavigationBarItem]
/// widgets and the [currentIndex] is set to index 1. The color of the selected /// widgets and the [currentIndex] is set to index 1. The color of the selected
...@@ -78,50 +77,42 @@ enum BottomNavigationBarType { ...@@ -78,50 +77,42 @@ enum BottomNavigationBarType {
/// the [Scaffold]. /// the [Scaffold].
/// ///
/// ```dart /// ```dart
/// class MyHomePage extends StatefulWidget { /// int _selectedIndex = 1;
/// MyHomePage({Key key}) : super(key: key); /// static const List<Widget> _widgetOptions = const <Widget>[
/// Text('Index 0: Home'),
/// Text('Index 1: Business'),
/// Text('Index 2: School'),
/// ];
/// ///
/// @override /// void _onItemTapped(int index) {
/// _MyHomePageState createState() => _MyHomePageState(); /// setState(() {
/// _selectedIndex = index;
/// });
/// } /// }
/// ///
/// class _MyHomePageState extends State<MyHomePage> { /// @override
/// int _selectedIndex = 1; /// Widget build(BuildContext context) {
/// final _widgetOptions = [ /// return Scaffold(
/// Text('Index 0: Home'), /// appBar: AppBar(
/// Text('Index 1: Business'), /// title: Text('BottomNavigationBar Sample'),
/// Text('Index 2: School'), /// ),
/// ]; /// body: Center(
/// /// child: _widgetOptions.elementAt(_selectedIndex),
/// @override /// ),
/// Widget build(BuildContext context) { /// bottomNavigationBar: BottomNavigationBar(
/// return Scaffold( /// items: <BottomNavigationBarItem>[
/// appBar: AppBar( /// BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('Home')),
/// title: Text('BottomNavigationBar Sample'), /// BottomNavigationBarItem(icon: Icon(Icons.business), title: Text('Business')),
/// ), /// BottomNavigationBarItem(icon: Icon(Icons.school), title: Text('School')),
/// body: Center( /// ],
/// child: _widgetOptions.elementAt(_selectedIndex), /// currentIndex: _selectedIndex,
/// ), /// fixedColor: Colors.deepPurple,
/// bottomNavigationBar: BottomNavigationBar( /// onTap: _onItemTapped,
/// items: <BottomNavigationBarItem>[ /// ),
/// BottomNavigationBarItem(icon: Icon(Icons.home), title: Text('Home')), /// );
/// BottomNavigationBarItem(icon: Icon(Icons.business), title: Text('Business')),
/// BottomNavigationBarItem(icon: Icon(Icons.school), title: Text('School')),
/// ],
/// currentIndex: _selectedIndex,
/// fixedColor: Colors.deepPurple,
/// onTap: _onItemTapped,
/// ),
/// );
/// }
///
/// void _onItemTapped(int index) {
/// setState(() {
/// _selectedIndex = index;
/// });
/// }
/// } /// }
/// ``` /// ```
/// {@end-tool}
/// ///
/// See also: /// See also:
/// ///
......
...@@ -19,60 +19,72 @@ import 'theme.dart'; ...@@ -19,60 +19,72 @@ import 'theme.dart';
/// some text describing a musical, and the other with buttons for buying /// some text describing a musical, and the other with buttons for buying
/// tickets or listening to the show.](https://flutter.github.io/assets-for-api-docs/assets/material/card.png) /// tickets or listening to the show.](https://flutter.github.io/assets-for-api-docs/assets/material/card.png)
/// ///
/// {@tool snippet --template=stateless_widget} /// {@tool snippet --template=stateless_widget_scaffold}
/// ///
/// This sample shows creation of a [Card] widget that shows album information /// This sample shows creation of a [Card] widget that shows album information
/// and two actions. /// and two actions.
/// ///
/// ```dart /// ```dart
/// Center( /// Widget build(BuildContext context) {
/// child: Card( /// return Center(
/// child: Column( /// child: Card(
/// mainAxisSize: MainAxisSize.min, /// child: Column(
/// children: <Widget>[ /// mainAxisSize: MainAxisSize.min,
/// const ListTile( /// children: <Widget>[
/// leading: Icon(Icons.album), /// const ListTile(
/// title: Text('The Enchanted Nightingale'), /// leading: Icon(Icons.album),
/// subtitle: Text('Music by Julie Gable. Lyrics by Sidney Stein.'), /// title: Text('The Enchanted Nightingale'),
/// ), /// subtitle: Text('Music by Julie Gable. Lyrics by Sidney Stein.'),
/// ButtonTheme.bar( // make buttons use the appropriate styles for cards
/// child: ButtonBar(
/// children: <Widget>[
/// FlatButton(
/// child: const Text('BUY TICKETS'),
/// onPressed: () { /* ... */ },
/// ),
/// FlatButton(
/// child: const Text('LISTEN'),
/// onPressed: () { /* ... */ },
/// ),
/// ],
/// ), /// ),
/// ), /// ButtonTheme.bar( // make buttons use the appropriate styles for cards
/// ], /// child: ButtonBar(
/// children: <Widget>[
/// FlatButton(
/// child: const Text('BUY TICKETS'),
/// onPressed: () { /* ... */ },
/// ),
/// FlatButton(
/// child: const Text('LISTEN'),
/// onPressed: () { /* ... */ },
/// ),
/// ],
/// ),
/// ),
/// ],
/// ),
/// ), /// ),
/// ), /// );
/// ) /// }
/// ``` /// ```
/// {@end-tool} /// {@end-tool}
/// ///
/// Sometimes the primary action area of a card is the card itself. Cards can be /// Sometimes the primary action area of a card is the card itself. Cards can be
/// one large touch target that shows a detail screen when tapped. /// one large touch target that shows a detail screen when tapped.
/// ///
/// {@tool snippet --template=stateless_widget} /// {@tool snippet --template=stateless_widget_scaffold}
/// ///
/// This sample shows creation of a [Card] widget that can be tapped. When /// This sample shows creation of a [Card] widget that can be tapped. When
/// tapped this [Card]'s [InkWell] displays an "ink splash" that fills the /// tapped this [Card]'s [InkWell] displays an "ink splash" that fills the
/// entire card. /// entire card.
/// ///
/// ```dart /// ```dart
/// Card( /// Widget build(BuildContext context) {
/// child: InkWell( /// return Center(
/// splashColor: Colors.blue.withAlpha(30), /// child: Card(
/// onTap: () { /* ... */ }, /// child: InkWell(
/// child: Text('A card that can be tapped'), /// splashColor: Colors.blue.withAlpha(30),
/// ), /// onTap: () {
/// ) /// print('Card tapped.');
/// },
/// child: Container(
/// width: 300,
/// height: 100,
/// child: Text('A card that can be tapped'),
/// ),
/// ),
/// ),
/// );
/// }
/// ``` /// ```
/// ///
/// {@end-tool} /// {@end-tool}
......
...@@ -151,7 +151,7 @@ abstract class DeletableChipAttributes { ...@@ -151,7 +151,7 @@ abstract class DeletableChipAttributes {
/// that the user tapped the delete button. In order to delete the chip, you /// that the user tapped the delete button. In order to delete the chip, you
/// have to do something similar to the following sample: /// have to do something similar to the following sample:
/// ///
/// {@tool snippet --template=stateful_widget} /// {@tool snippet --template=stateful_widget_scaffold}
/// ///
/// This sample shows how to use [onDeleted] to remove an entry when the /// This sample shows how to use [onDeleted] to remove an entry when the
/// delete button is tapped. /// delete button is tapped.
...@@ -207,7 +207,7 @@ abstract class DeletableChipAttributes { ...@@ -207,7 +207,7 @@ abstract class DeletableChipAttributes {
/// ```dart /// ```dart
/// @override /// @override
/// Widget build(BuildContext context) { /// Widget build(BuildContext context) {
/// return CastList(); /// return Center(child: CastList());
/// } /// }
/// ``` /// ```
/// {@end-tool} /// {@end-tool}
......
...@@ -485,7 +485,7 @@ class DropdownButtonHideUnderline extends InheritedWidget { ...@@ -485,7 +485,7 @@ class DropdownButtonHideUnderline extends InheritedWidget {
/// dropdown's value. It should also call [State.setState] to rebuild the /// dropdown's value. It should also call [State.setState] to rebuild the
/// dropdown with the new value. /// dropdown with the new value.
/// ///
/// {@tool snippet --template=stateful_widget} /// {@tool snippet --template=stateful_widget_scaffold}
/// ///
/// This sample shows a `DropdownButton` whose value is one of /// This sample shows a `DropdownButton` whose value is one of
/// "One", "Two", "Free", or "Four". /// "One", "Two", "Free", or "Four".
......
...@@ -37,7 +37,7 @@ const double _kMinButtonSize = 48.0; ...@@ -37,7 +37,7 @@ const double _kMinButtonSize = 48.0;
/// requirements in the Material Design specification. The [alignment] controls /// requirements in the Material Design specification. The [alignment] controls
/// how the icon itself is positioned within the hit region. /// how the icon itself is positioned within the hit region.
/// ///
/// {@tool snippet --template=stateful_widget} /// {@tool snippet --template=stateful_widget_scaffold}
/// ///
/// This sample shows an `IconButton` that uses the Material icon "volume_up" to /// This sample shows an `IconButton` that uses the Material icon "volume_up" to
/// increase the volume. /// increase the volume.
...@@ -83,7 +83,7 @@ const double _kMinButtonSize = 48.0; ...@@ -83,7 +83,7 @@ const double _kMinButtonSize = 48.0;
/// the underlying [Material] along with the splash and highlight /// the underlying [Material] along with the splash and highlight
/// [InkResponse] contributed by descendant widgets. /// [InkResponse] contributed by descendant widgets.
/// ///
/// {@tool snippet --template=stateless_widget} /// {@tool snippet --template=stateless_widget_scaffold}
/// ///
/// In this sample the icon button's background color is defined with an [Ink] /// In this sample the icon button's background color is defined with an [Ink]
/// widget whose child is an [IconButton]. The icon button's filled background /// widget whose child is an [IconButton]. The icon button's filled background
...@@ -91,17 +91,25 @@ const double _kMinButtonSize = 48.0; ...@@ -91,17 +91,25 @@ const double _kMinButtonSize = 48.0;
/// button is. /// button is.
/// ///
/// ```dart /// ```dart
/// Ink( /// Widget build(BuildContext context) {
/// decoration: ShapeDecoration( /// return Center(
/// color: Colors.purple, /// child: Container(
/// shape: CircleBorder(), /// child: Ink(
/// ), /// decoration: ShapeDecoration(
/// child: IconButton( /// color: Colors.lightBlue,
/// icon: Icon(Icons.android), /// shape: CircleBorder(),
/// color: Colors.white, /// ),
/// onPressed: () { print("filled background"); }, /// child: IconButton(
/// ), /// icon: Icon(Icons.android),
/// ) /// color: Colors.white,
/// onPressed: () {
/// print("filled background");
/// },
/// ),
/// ),
/// ),
/// );
/// }
/// ``` /// ```
/// {@end-tool} /// {@end-tool}
/// ///
......
...@@ -31,43 +31,45 @@ import 'theme_data.dart'; ...@@ -31,43 +31,45 @@ import 'theme_data.dart';
/// Raised buttons have a minimum size of 88.0 by 36.0 which can be overidden /// Raised buttons have a minimum size of 88.0 by 36.0 which can be overidden
/// with [ButtonTheme]. /// with [ButtonTheme].
/// ///
/// {@tool snippet --template=stateless_widget} /// {@tool snippet --template=stateless_widget_scaffold}
/// ///
/// This sample shows how to render a disabled RaisedButton, an enabled RaisedButton /// This sample shows how to render a disabled RaisedButton, an enabled RaisedButton
/// and lastly a RaisedButton with gradient background. /// and lastly a RaisedButton with gradient background.
/// ///
/// ```dart /// ```dart
/// Scaffold( /// Widget build(BuildContext context) {
/// body: Center( /// return Scaffold(
/// child: Column( /// body: Center(
/// mainAxisSize: MainAxisSize.min, /// child: Column(
/// children: <Widget>[ /// mainAxisSize: MainAxisSize.min,
/// RaisedButton( /// children: <Widget>[
/// onPressed: null, /// RaisedButton(
/// child: const Text('Disabled Button'), /// onPressed: null,
/// ), /// child: const Text('Disabled Button'),
/// RaisedButton( /// ),
/// onPressed: () {}, /// RaisedButton(
/// child: const Text('Enabled Button'), /// onPressed: () {},
/// ), /// child: const Text('Enabled Button'),
/// RaisedButton( /// ),
/// onPressed: () {}, /// RaisedButton(
/// textColor: Colors.white, /// onPressed: () {},
/// padding: const EdgeInsets.all(0.0), /// textColor: Colors.white,
/// child: Container( /// padding: const EdgeInsets.all(0.0),
/// decoration: const BoxDecoration( /// child: Container(
/// gradient: LinearGradient( /// decoration: const BoxDecoration(
/// colors: <Color>[Colors.red, Colors.green, Colors.blue], /// gradient: LinearGradient(
/// colors: <Color>[Colors.red, Colors.green, Colors.blue],
/// ),
/// ), /// ),
/// padding: const EdgeInsets.all(10.0),
/// child: Text('Gradient Button'),
/// ), /// ),
/// padding: const EdgeInsets.all(10.0),
/// child: Text('Gradient Button'),
/// ), /// ),
/// ), /// ],
/// ], /// ),
/// ), /// ),
/// ), /// );
/// ) /// }
/// ``` /// ```
/// {@end-tool} /// {@end-tool}
/// ///
......
...@@ -26,11 +26,11 @@ import 'snack_bar.dart'; ...@@ -26,11 +26,11 @@ import 'snack_bar.dart';
import 'theme.dart'; import 'theme.dart';
// Examples can assume: // Examples can assume:
// TabController tabController // TabController tabController;
// void setState(VoidCallback fn) { } // void setState(VoidCallback fn) { }
// String appBarTitle // String appBarTitle;
// int tabCount // int tabCount;
// TickerProvider tickerProvider // TickerProvider tickerProvider;
const FloatingActionButtonLocation _kDefaultFloatingActionButtonLocation = FloatingActionButtonLocation.endFloat; const FloatingActionButtonLocation _kDefaultFloatingActionButtonLocation = FloatingActionButtonLocation.endFloat;
const FloatingActionButtonAnimator _kDefaultFloatingActionButtonAnimator = FloatingActionButtonAnimator.scaling; const FloatingActionButtonAnimator _kDefaultFloatingActionButtonAnimator = FloatingActionButtonAnimator.scaling;
...@@ -668,8 +668,7 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr ...@@ -668,8 +668,7 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
/// [ScaffoldState] for the current [BuildContext] via [Scaffold.of] and use the /// [ScaffoldState] for the current [BuildContext] via [Scaffold.of] and use the
/// [ScaffoldState.showSnackBar] and [ScaffoldState.showBottomSheet] functions. /// [ScaffoldState.showSnackBar] and [ScaffoldState.showBottomSheet] functions.
/// ///
/// {@tool snippet --template=stateful_widget} /// {@tool snippet --template=stateful_widget_material}
///
/// This example shows a [Scaffold] with an [AppBar], a [BottomAppBar] and a /// This example shows a [Scaffold] with an [AppBar], a [BottomAppBar] and a
/// [FloatingActionButton]. The [body] is a [Text] placed in a [Center] in order /// [FloatingActionButton]. The [body] is a [Text] placed in a [Center] in order
/// to center the text within the [Scaffold] and the [FloatingActionButton] is /// to center the text within the [Scaffold] and the [FloatingActionButton] is
...@@ -736,21 +735,21 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr ...@@ -736,21 +735,21 @@ class _FloatingActionButtonTransitionState extends State<_FloatingActionButtonTr
/// scaffold with a differently titled AppBar. It would be better to add a /// scaffold with a differently titled AppBar. It would be better to add a
/// listener to the [TabController] that updates the AppBar. /// listener to the [TabController] that updates the AppBar.
/// ///
/// ## Sample Code /// {@tool sample}
///
/// Add a listener to the app's tab controller so that the [AppBar] title of the /// Add a listener to the app's tab controller so that the [AppBar] title of the
/// app's one and only scaffold is reset each time a new tab is selected. /// app's one and only scaffold is reset each time a new tab is selected.
/// ///
/// ```dart /// ```dart
/// tabController = TabController(vsync: tickerProvider, length: tabCount)..addListener(() { /// TabController(vsync: tickerProvider, length: tabCount)..addListener(() {
/// if (!tabController.indexIsChanging) { /// if (!tabController.indexIsChanging) {
/// setState(() { /// setState(() {
/// // Rebuild the enclosing scaffold with a new AppBar title /// // Rebuild the enclosing scaffold with a new AppBar title
/// appBarTitle = 'Tab ${tabController.index}'; /// appBarTitle = 'Tab ${tabController.index}';
/// }); /// });
/// } /// }
/// }); /// })
/// ``` /// ```
/// {@end-tool}
/// ///
/// Although there are some use cases, like a presentation app that /// Although there are some use cases, like a presentation app that
/// shows embedded flutter content, where nested scaffolds are /// shows embedded flutter content, where nested scaffolds are
...@@ -949,30 +948,68 @@ class Scaffold extends StatefulWidget { ...@@ -949,30 +948,68 @@ class Scaffold extends StatefulWidget {
/// The state from the closest instance of this class that encloses the given context. /// The state from the closest instance of this class that encloses the given context.
/// ///
/// Typical usage is as follows: /// {@tool snippet --template=freeform}
/// Typical usage of the [Scaffold.of] function is to call it from within the
/// `build` method of a child of a [Scaffold].
///
/// ```dart imports
/// import 'package:flutter/material.dart';
/// ```
///
/// ```dart main
/// void main() => runApp(MyApp());
/// ```
///
/// ```dart preamble
/// class MyApp extends StatelessWidget {
/// // This widget is the root of your application.
/// @override
/// Widget build(BuildContext context) {
/// return MaterialApp(
/// title: 'Flutter Code Sample for Scaffold.of.',
/// theme: ThemeData(
/// primarySwatch: Colors.blue,
/// ),
/// home: Scaffold(
/// body: MyScaffoldBody(),
/// appBar: AppBar(title: Text('Scaffold.of Example')),
/// ),
/// color: Colors.white,
/// );
/// }
/// }
/// ```
/// ///
/// ```dart /// ```dart
/// @override /// class MyScaffoldBody extends StatelessWidget {
/// Widget build(BuildContext context) { /// @override
/// return RaisedButton( /// Widget build(BuildContext context) {
/// child: Text('SHOW A SNACKBAR'), /// return Center(
/// onPressed: () { /// child: RaisedButton(
/// Scaffold.of(context).showSnackBar(SnackBar( /// child: Text('SHOW A SNACKBAR'),
/// content: Text('Hello!'), /// onPressed: () {
/// )); /// Scaffold.of(context).showSnackBar(
/// }, /// SnackBar(
/// ); /// content: Text('Have a snack!'),
/// ),
/// );
/// },
/// ),
/// );
/// }
/// } /// }
/// ``` /// ```
/// {@end-tool}
/// ///
/// {@tool snippet --template=stateless_widget_material}
/// When the [Scaffold] is actually created in the same `build` function, the /// When the [Scaffold] is actually created in the same `build` function, the
/// `context` argument to the `build` function can't be used to find the /// `context` argument to the `build` function can't be used to find the
/// [Scaffold] (since it's "above" the widget being returned). In such cases, /// [Scaffold] (since it's "above" the widget being returned in the widget
/// the following technique with a [Builder] can be used to provide a new /// tree). In such cases, the following technique with a [Builder] can be used
/// scope with a [BuildContext] that is "under" the [Scaffold]: /// to provide a new scope with a [BuildContext] that is "under" the
/// [Scaffold]:
/// ///
/// ```dart /// ```dart
/// @override
/// Widget build(BuildContext context) { /// Widget build(BuildContext context) {
/// return Scaffold( /// return Scaffold(
/// appBar: AppBar( /// appBar: AppBar(
...@@ -987,7 +1024,7 @@ class Scaffold extends StatefulWidget { ...@@ -987,7 +1024,7 @@ class Scaffold extends StatefulWidget {
/// child: Text('SHOW A SNACKBAR'), /// child: Text('SHOW A SNACKBAR'),
/// onPressed: () { /// onPressed: () {
/// Scaffold.of(context).showSnackBar(SnackBar( /// Scaffold.of(context).showSnackBar(SnackBar(
/// content: Text('Hello!'), /// content: Text('Have a snack!'),
/// )); /// ));
/// }, /// },
/// ), /// ),
...@@ -997,6 +1034,7 @@ class Scaffold extends StatefulWidget { ...@@ -997,6 +1034,7 @@ class Scaffold extends StatefulWidget {
/// ); /// );
/// } /// }
/// ``` /// ```
/// {@end-tool}
/// ///
/// A more efficient solution is to split your build function into several /// A more efficient solution is to split your build function into several
/// widgets. This introduces a new context from which you can obtain the /// widgets. This introduces a new context from which you can obtain the
......
...@@ -192,44 +192,47 @@ class Stepper extends StatefulWidget { ...@@ -192,44 +192,47 @@ class Stepper extends StatefulWidget {
/// This callback which takes in a context and two functions,[onStepContinue] /// This callback which takes in a context and two functions,[onStepContinue]
/// and [onStepCancel]. These can be used to control the stepper. /// and [onStepCancel]. These can be used to control the stepper.
/// ///
/// ## Sample Code: /// {@tool snippet --template=stateless_widget_scaffold}
/// Creates a stepper control with custom buttons. /// Creates a stepper control with custom buttons.
/// ///
/// ```dart /// ```dart
/// Stepper( /// Widget build(BuildContext context) {
/// controlsBuilder: /// return Stepper(
/// (BuildContext context, {VoidCallback onStepContinue, VoidCallback onStepCancel}) { /// controlsBuilder:
/// return Row( /// (BuildContext context, {VoidCallback onStepContinue, VoidCallback onStepCancel}) {
/// children: <Widget>[ /// return Row(
/// FlatButton( /// children: <Widget>[
/// onPressed: onStepContinue, /// FlatButton(
/// child: const Text('My Awesome Continue Message!'), /// onPressed: onStepContinue,
/// ), /// child: const Text('CONTINUE'),
/// FlatButton( /// ),
/// onPressed: onStepCancel, /// FlatButton(
/// child: const Text('My Awesome Cancel Message!'), /// onPressed: onStepCancel,
/// ), /// child: const Text('CANCEL'),
/// ], /// ),
/// ), /// ],
/// }, /// );
/// steps: const <Step>[ /// },
/// Step( /// steps: const <Step>[
/// title: Text('A'), /// Step(
/// content: SizedBox( /// title: Text('A'),
/// width: 100.0, /// content: SizedBox(
/// height: 100.0, /// width: 100.0,
/// height: 100.0,
/// ),
/// ), /// ),
/// ), /// Step(
/// Step( /// title: Text('B'),
/// title: Text('B'), /// content: SizedBox(
/// content: SizedBox( /// width: 100.0,
/// width: 100.0, /// height: 100.0,
/// height: 100.0, /// ),
/// ), /// ),
/// ), /// ],
/// ], /// );
/// ) /// }
/// ``` /// ```
/// {@end-tool}
final ControlsWidgetBuilder controlsBuilder; final ControlsWidgetBuilder controlsBuilder;
@override @override
......
...@@ -19,6 +19,9 @@ import 'view.dart'; ...@@ -19,6 +19,9 @@ import 'view.dart';
export 'package:flutter/gestures.dart' show HitTestResult; export 'package:flutter/gestures.dart' show HitTestResult;
// Examples can assume:
// dynamic context;
/// The glue between the render tree and the Flutter engine. /// The glue between the render tree and the Flutter engine.
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable { mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
@override @override
...@@ -175,26 +178,30 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture ...@@ -175,26 +178,30 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Gesture
/// The current platform brightness can be queried either from a Flutter /// The current platform brightness can be queried either from a Flutter
/// binding, or from a [MediaQuery] widget. /// binding, or from a [MediaQuery] widget.
/// ///
/// ## Sample Code /// {@tool sample}
/// /// Querying [Window.platformBrightness].
/// Querying [Window.platformBrightness]:
/// ///
/// ```dart /// ```dart
/// final Brightness brightness = WidgetsBinding.instance.window.platformBrightness; /// final Brightness brightness = WidgetsBinding.instance.window.platformBrightness;
/// ``` /// ```
/// {@end-tool}
/// ///
/// Querying [MediaQuery] directly: /// {@tool sample}
/// Querying [MediaQuery] directly.
/// ///
/// ```dart /// ```dart
/// final Brightness brightness = MediaQuery.platformBrightnessOf(context); /// final Brightness brightness = MediaQuery.platformBrightnessOf(context);
/// ``` /// ```
/// {@end-tool}
/// ///
/// Querying [MediaQueryData]: /// {@tool sample}
/// Querying [MediaQueryData].
/// ///
/// ```dart /// ```dart
/// final MediaQueryData mediaQueryData = MediaQuery.of(context); /// final MediaQueryData mediaQueryData = MediaQuery.of(context);
/// final Brightness brightness = mediaQueryData.platformBrightness; /// final Brightness brightness = mediaQueryData.platformBrightness;
/// ``` /// ```
/// {@end-tool}
/// ///
/// See [Window.onPlatformBrightnessChanged]. /// See [Window.onPlatformBrightnessChanged].
/// {@endtemplate} /// {@endtemplate}
......
...@@ -1550,11 +1550,21 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -1550,11 +1550,21 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
/// describes how high the box is that this [SemanticsNode] occupies in three /// describes how high the box is that this [SemanticsNode] occupies in three
/// dimensional space. The two other dimensions are defined by [rect]. /// dimensional space. The two other dimensions are defined by [rect].
/// ///
/// ## Sample Code /// {@tool sample}
///
/// The following code stacks three [PhysicalModel]s on top of each other /// The following code stacks three [PhysicalModel]s on top of each other
/// separated by none-zero elevations: /// separated by non-zero elevations.
///
/// [PhysicalModel] C is elevated 10.0 above [PhysicalModel] B, which in turn
/// is elevated 5.0 above [PhysicalModel] A. The side view of this
/// constellation looks as follows:
///
/// ![A diagram illustrating the elevations of three PhysicalModels and their
/// corresponding SemanticsNodes.](https://flutter.github.io/assets-for-api-docs/assets/semantics/SemanticsNode.thickness.png)
/// ///
/// In this example the [RenderObject]s for [PhysicalModel] C and B share one
/// [SemanticsNode] Y. Given the elevations of those [RenderObject]s, this
/// [SemanticsNode] has a [thickness] of 10.0 and an elevation of 5.0 over
/// its parent [SemanticsNode] X.
/// ```dart /// ```dart
/// PhysicalModel( // A /// PhysicalModel( // A
/// color: Colors.amber, /// color: Colors.amber,
...@@ -1571,20 +1581,9 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -1571,20 +1581,9 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
/// ), /// ),
/// ), /// ),
/// ), /// ),
/// ); /// )
/// ``` /// ```
/// /// {@end-tool}
/// [PhysicalModel] C is elevated 10.0 above [PhysicalModel] B, which in turn
/// is elevated 5.0 above [PhysicalModel] A. The side view of this
/// constellation looks as follows:
///
/// ![A diagram illustrating the elevations of three PhysicalModels and their
/// corresponding SemanticsNodes.](https://flutter.github.io/assets-for-api-docs/assets/semantics/SemanticsNode.thickness.png)
///
/// In this example the [RenderObject]s for [PhysicalModel] C and B share one
/// [SemanticsNode] Y. Given the elevations of those [RenderObject]s, this
/// [SemanticsNode] has a [thickness] of 10.0 and an elevation of 5.0 over
/// its parent [SemanticsNode] X.
/// ///
/// See also: /// See also:
/// ///
......
...@@ -32,7 +32,7 @@ import 'package:flutter/foundation.dart'; ...@@ -32,7 +32,7 @@ import 'package:flutter/foundation.dart';
/// look at the physical key to make sure that regardless of the character the /// look at the physical key to make sure that regardless of the character the
/// key produces, you got the key that is in that location on the keyboard. /// key produces, you got the key that is in that location on the keyboard.
/// ///
/// {@tool snippet --template=stateful_widget} /// {@tool snippet --template=stateful_widget_scaffold}
/// This example shows how to detect if the user has selected the logical "Q" /// This example shows how to detect if the user has selected the logical "Q"
/// key. /// key.
/// ///
...@@ -1655,7 +1655,7 @@ class LogicalKeyboardKey extends Diagnosticable { ...@@ -1655,7 +1655,7 @@ class LogicalKeyboardKey extends Diagnosticable {
/// looking for "the key next next to the TAB key", since on a French keyboard, /// looking for "the key next next to the TAB key", since on a French keyboard,
/// the key next to the TAB key has an "A" on it. /// the key next to the TAB key has an "A" on it.
/// ///
/// {@tool snippet --template=stateful_widget} /// {@tool snippet --template=stateful_widget_scaffold}
/// This example shows how to detect if the user has selected the physical key /// This example shows how to detect if the user has selected the physical key
/// to the right of the CAPS LOCK key. /// to the right of the CAPS LOCK key.
/// ///
......
...@@ -311,15 +311,15 @@ class SystemChrome { ...@@ -311,15 +311,15 @@ class SystemChrome {
/// If a particular style is not supported on the platform, selecting it will /// If a particular style is not supported on the platform, selecting it will
/// have no effect. /// have no effect.
/// ///
/// ## Sample Code /// {@tool sample}
///
/// ```dart /// ```dart
/// @override /// @override
/// Widget build(BuildContext context) { /// Widget build(BuildContext context) {
/// SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark); /// SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark);
/// return /* ... */; /// return Placeholder();
/// } /// }
/// ``` /// ```
/// {@end-tool}
/// ///
/// For more complex control of the system overlay styles, consider using /// For more complex control of the system overlay styles, consider using
/// an [AnnotatedRegion] widget instead of calling [setSystemUiOverlayStyle] /// an [AnnotatedRegion] widget instead of calling [setSystemUiOverlayStyle]
...@@ -329,47 +329,46 @@ class SystemChrome { ...@@ -329,47 +329,46 @@ class SystemChrome {
/// navigation bar and synthesize them into a single style. This can be used /// navigation bar and synthesize them into a single style. This can be used
/// to configure the system styles when an app bar is not used. /// to configure the system styles when an app bar is not used.
/// ///
/// {@tool snippet --template=stateful_widget} /// {@tool snippet --template=stateful_widget_material}
/// The following example creates a widget that changes the status bar color /// The following example creates a widget that changes the status bar color
/// to a random value on Android. /// to a random value on Android.
/// ///
/// ```dart imports /// ```dart imports
/// import 'package:flutter/services.dart'; /// import 'package:flutter/services.dart';
/// import 'dart:math' as math; /// import 'dart:math' as math;
/// ``` /// ```
/// ///
/// ```dart /// ```dart
/// final _random = math.Random(); /// final _random = math.Random();
/// SystemUiOverlayStyle _currentStyle = SystemUiOverlayStyle.light; /// SystemUiOverlayStyle _currentStyle = SystemUiOverlayStyle.light;
/// ///
/// void _changeColor() { /// void _changeColor() {
/// final color = Color.fromRGBO( /// final color = Color.fromRGBO(
/// _random.nextInt(255), /// _random.nextInt(255),
/// _random.nextInt(255), /// _random.nextInt(255),
/// _random.nextInt(255), /// _random.nextInt(255),
/// 1.0, /// 1.0,
/// ); /// );
/// setState(() { /// setState(() {
/// _currentStyle = SystemUiOverlayStyle.dark.copyWith( /// _currentStyle = SystemUiOverlayStyle.dark.copyWith(
/// statusBarColor: color, /// statusBarColor: color,
/// ); /// );
/// }); /// });
/// } /// }
/// ///
/// @override /// @override
/// Widget build(BuildContext context) { /// Widget build(BuildContext context) {
/// return Scaffold( /// return AnnotatedRegion(
/// body: AnnotatedRegion( /// value: _currentStyle,
/// value: _currentStyle, /// child: Center(
/// child: Center( /// child: RaisedButton(
/// child: RaisedButton( /// child: const Text('Change Color'),
/// child: const Text('Change Color'), /// onPressed: _changeColor,
/// onPressed: _changeColor, /// ),
/// ), /// ),
/// ), /// );
/// ), /// }
/// ); /// ```
/// }
/// {@end-tool} /// {@end-tool}
/// ///
/// See also: /// See also:
......
...@@ -5046,7 +5046,7 @@ class WidgetToRenderBoxAdapter extends LeafRenderObjectWidget { ...@@ -5046,7 +5046,7 @@ class WidgetToRenderBoxAdapter extends LeafRenderObjectWidget {
/// If it has a child, this widget defers to the child for sizing behavior. If /// If it has a child, this widget defers to the child for sizing behavior. If
/// it does not have a child, it grows to fit the parent instead. /// it does not have a child, it grows to fit the parent instead.
/// ///
/// {@tool snippet --template=stateful_widget} /// {@tool snippet --template=stateful_widget_scaffold}
/// This example makes a [Container] react to being entered by a mouse /// This example makes a [Container] react to being entered by a mouse
/// pointer, showing a count of the number of entries and exits. /// pointer, showing a count of the number of entries and exits.
/// ///
...@@ -5081,32 +5081,27 @@ class WidgetToRenderBoxAdapter extends LeafRenderObjectWidget { ...@@ -5081,32 +5081,27 @@ class WidgetToRenderBoxAdapter extends LeafRenderObjectWidget {
/// ///
/// @override /// @override
/// Widget build(BuildContext context) { /// Widget build(BuildContext context) {
/// return Scaffold( /// return Center(
/// appBar: AppBar( /// child: ConstrainedBox(
/// title: Text('Hover Example'), /// constraints: new BoxConstraints.tight(Size(300.0, 200.0)),
/// ), /// child: Listener(
/// body: Center( /// onPointerEnter: _incrementCounter,
/// child: ConstrainedBox( /// onPointerHover: _updateLocation,
/// constraints: new BoxConstraints.tight(Size(300.0, 200.0)), /// onPointerExit: _decrementCounter,
/// child: Listener( /// child: Container(
/// onPointerEnter: _incrementCounter, /// color: Colors.lightBlueAccent,
/// onPointerHover: _updateLocation, /// child: Column(
/// onPointerExit: _decrementCounter, /// mainAxisAlignment: MainAxisAlignment.center,
/// child: Container( /// children: <Widget>[
/// color: Colors.lightBlueAccent, /// Text('You have pointed at this box this many times:'),
/// child: Column( /// Text(
/// mainAxisAlignment: MainAxisAlignment.center, /// '$_enterCounter Entries\n$_exitCounter Exits',
/// children: <Widget>[ /// style: Theme.of(context).textTheme.display1,
/// Text('You have pointed at this box this many times:'), /// ),
/// Text( /// Text(
/// '$_enterCounter Entries\n$_exitCounter Exits', /// 'The cursor is here: (${x.toStringAsFixed(2)}, ${y.toStringAsFixed(2)})',
/// style: Theme.of(context).textTheme.display1, /// ),
/// ), /// ],
/// Text(
/// 'The cursor is here: (${x.toStringAsFixed(2)}, ${y.toStringAsFixed(2)})',
/// ),
/// ],
/// ),
/// ), /// ),
/// ), /// ),
/// ), /// ),
......
...@@ -594,20 +594,30 @@ class NavigatorObserver { ...@@ -594,20 +594,30 @@ class NavigatorObserver {
/// in this situation, but it's a real world example where nested [Navigator]s /// in this situation, but it's a real world example where nested [Navigator]s
/// are used. /// are used.
/// ///
/// #### Sample Code /// {@tool snippet --template=freeform}
///
/// The following example demonstrates how a nested [Navigator] can be used to /// The following example demonstrates how a nested [Navigator] can be used to
/// present a standalone user registration journey. /// present a standalone user registration journey.
/// ///
/// Even though this example uses two [Navigator]s to demonstrate nested /// Even though this example uses two [Navigator]s to demonstrate nested
/// [Navigator]s, a similar result is possible using only a single [Navigator]. /// [Navigator]s, a similar result is possible using only a single [Navigator].
/// ///
/// Run this example with `flutter run --route=/signup` to start it with
/// the signup flow instead of on the home page.
///
/// ```dart imports
/// import 'package:flutter/material.dart';
/// ```
///
/// ```dart main
/// void main() => runApp(new MyApp());
/// ```
///
/// ```dart /// ```dart
/// class MyApp extends StatelessWidget { /// class MyApp extends StatelessWidget {
/// @override /// @override
/// Widget build(BuildContext context) { /// Widget build(BuildContext context) {
/// return MaterialApp( /// return MaterialApp(
/// // ...some parameters omitted... /// title: 'Flutter Code Sample for Navigator',
/// // MaterialApp contains our top-level Navigator /// // MaterialApp contains our top-level Navigator
/// initialRoute: '/', /// initialRoute: '/',
/// routes: { /// routes: {
...@@ -618,44 +628,104 @@ class NavigatorObserver { ...@@ -618,44 +628,104 @@ class NavigatorObserver {
/// } /// }
/// } /// }
/// ///
/// class HomePage extends StatelessWidget {
/// @override
/// Widget build(BuildContext context) {
/// return DefaultTextStyle(
/// style: Theme.of(context).textTheme.display1,
/// child: Container(
/// color: Colors.white,
/// alignment: Alignment.center,
/// child: Text('Home Page'),
/// ),
/// );
/// }
/// }
///
/// class CollectPersonalInfoPage extends StatelessWidget {
/// @override
/// Widget build(BuildContext context) {
/// return DefaultTextStyle(
/// style: Theme.of(context).textTheme.display1,
/// child: GestureDetector(
/// onTap: () {
/// // This moves from the personal info page to the credentials page,
/// // replacing this page with that one.
/// Navigator.of(context)
/// .pushReplacementNamed('signup/choose_credentials');
/// },
/// child: Container(
/// color: Colors.lightBlue,
/// alignment: Alignment.center,
/// child: Text('Collect Personal Info Page'),
/// ),
/// ),
/// );
/// }
/// }
///
/// class ChooseCredentialsPage extends StatelessWidget {
/// const ChooseCredentialsPage({
/// this.onSignupComplete,
/// });
///
/// final VoidCallback onSignupComplete;
///
/// @override
/// Widget build(BuildContext context) {
/// return GestureDetector(
/// onTap: onSignupComplete,
/// child: DefaultTextStyle(
/// style: Theme.of(context).textTheme.display1,
/// child: Container(
/// color: Colors.pinkAccent,
/// alignment: Alignment.center,
/// child: Text('Choose Credentials Page'),
/// ),
/// ),
/// );
/// }
/// }
///
/// class SignUpPage extends StatelessWidget { /// class SignUpPage extends StatelessWidget {
/// @override /// @override
/// Widget build(BuildContext context) { /// Widget build(BuildContext context) {
/// // SignUpPage builds its own Navigator which ends up being a nested /// // SignUpPage builds its own Navigator which ends up being a nested
/// // Navigator in our app. /// // Navigator in our app.
/// return Navigator( /// return Navigator(
/// initialRoute: 'signup/personal_info', /// initialRoute: 'signup/personal_info',
/// onGenerateRoute: (RouteSettings settings) { /// onGenerateRoute: (RouteSettings settings) {
/// WidgetBuilder builder; /// WidgetBuilder builder;
/// switch (settings.name) { /// switch (settings.name) {
/// case 'signup/personal_info': /// case 'signup/personal_info':
/// // Assume CollectPersonalInfoPage collects personal info and then /// // Assume CollectPersonalInfoPage collects personal info and then
/// // navigates to 'signup/choose_credentials'. /// // navigates to 'signup/choose_credentials'.
/// builder = (BuildContext _) => CollectPersonalInfoPage(); /// builder = (BuildContext _) => CollectPersonalInfoPage();
/// break; /// break;
/// case 'signup/choose_credentials': /// case 'signup/choose_credentials':
/// // Assume ChooseCredentialsPage collects new credentials and then /// // Assume ChooseCredentialsPage collects new credentials and then
/// // invokes 'onSignupComplete()'. /// // invokes 'onSignupComplete()'.
/// builder = (BuildContext _) => ChooseCredentialsPage( /// builder = (BuildContext _) => ChooseCredentialsPage(
/// onSignupComplete: () { /// onSignupComplete: () {
/// // Referencing Navigator.of(context) from here refers to the /// // Referencing Navigator.of(context) from here refers to the
/// // top level Navigator because SignUpPage is above the /// // top level Navigator because SignUpPage is above the
/// // nested Navigator that it created. Therefore, this pop() /// // nested Navigator that it created. Therefore, this pop()
/// // will pop the entire "sign up" journey and return to the /// // will pop the entire "sign up" journey and return to the
/// // "/" route, AKA HomePage. /// // "/" route, AKA HomePage.
/// Navigator.of(context).pop(); /// Navigator.of(context).pop();
/// }, /// },
/// ); /// );
/// break; /// break;
/// default: /// default:
/// throw Exception('Invalid route: ${settings.name}'); /// throw Exception('Invalid route: ${settings.name}');
/// } /// }
/// return MaterialPageRoute(builder: builder, settings: settings); /// return MaterialPageRoute(builder: builder, settings: settings);
/// }, /// },
/// ); /// );
/// } /// }
/// } /// }
/// ``` /// ```
/// {@end-tool}
/// ///
/// [Navigator.of] operates on the nearest ancestor [Navigator] from the given /// [Navigator.of] operates on the nearest ancestor [Navigator] from the given
/// [BuildContext]. Be sure to provide a [BuildContext] below the intended /// [BuildContext]. Be sure to provide a [BuildContext] below the intended
......
...@@ -80,43 +80,47 @@ import 'scrollable.dart'; ...@@ -80,43 +80,47 @@ import 'scrollable.dart';
/// with some remaining space to allocate as specified by its /// with some remaining space to allocate as specified by its
/// [Column.mainAxisAlignment] argument. /// [Column.mainAxisAlignment] argument.
/// ///
/// In this example, the children are spaced out equally, unless there's no /// {@tool snippet --template=stateless_widget}
/// more room, in which case they stack vertically and scroll. /// In this example, the children are spaced out equally, unless there's no more
/// /// room, in which case they stack vertically and scroll.
/// ```dart
/// LayoutBuilder(
/// builder: (BuildContext context, BoxConstraints viewportConstraints) {
/// return SingleChildScrollView(
/// child: ConstrainedBox(
/// constraints: BoxConstraints(
/// minHeight: viewportConstraints.maxHeight,
/// ),
/// child: Column(
/// mainAxisSize: MainAxisSize.min,
/// mainAxisAlignment: MainAxisAlignment.spaceAround,
/// children: <Widget>[
/// Container(
/// // A fixed-height child.
/// color: Colors.yellow,
/// height: 120.0,
/// ),
/// Container(
/// // Another fixed-height child.
/// color: Colors.green,
/// height: 120.0,
/// ),
/// ],
/// ),
/// ),
/// );
/// },
/// )
/// ```
/// ///
/// When using this technique, [Expanded] and [Flexible] are not useful, because /// When using this technique, [Expanded] and [Flexible] are not useful, because
/// in both cases the "available space" is infinite (since this is in a viewport). /// in both cases the "available space" is infinite (since this is in a viewport).
/// The next section describes a technique for providing a maximum height constraint. /// The next section describes a technique for providing a maximum height constraint.
/// ///
/// ```dart
/// Widget build(BuildContext context) {
/// return LayoutBuilder(
/// builder: (BuildContext context, BoxConstraints viewportConstraints) {
/// return SingleChildScrollView(
/// child: ConstrainedBox(
/// constraints: BoxConstraints(
/// minHeight: viewportConstraints.maxHeight,
/// ),
/// child: Column(
/// mainAxisSize: MainAxisSize.min,
/// mainAxisAlignment: MainAxisAlignment.spaceAround,
/// children: <Widget>[
/// Container(
/// // A fixed-height child.
/// color: const Color(0xff808000), // Yellow
/// height: 120.0,
/// ),
/// Container(
/// // Another fixed-height child.
/// color: const Color(0xff008000), // Green
/// height: 120.0,
/// ),
/// ],
/// ),
/// ),
/// );
/// },
/// );
/// }
/// ```
/// {@end-tool}
///
/// ### Expanding content to fit the viewport /// ### Expanding content to fit the viewport
/// ///
/// The following example builds on the previous one. In addition to providing a /// The following example builds on the previous one. In addition to providing a
...@@ -135,39 +139,6 @@ import 'scrollable.dart'; ...@@ -135,39 +139,6 @@ import 'scrollable.dart';
/// The widget that is to grow to fit the remaining space so provided is wrapped /// The widget that is to grow to fit the remaining space so provided is wrapped
/// in an [Expanded] widget. /// in an [Expanded] widget.
/// ///
/// ```dart
/// LayoutBuilder(
/// builder: (BuildContext context, BoxConstraints viewportConstraints) {
/// return SingleChildScrollView(
/// child: ConstrainedBox(
/// constraints: BoxConstraints(
/// minHeight: viewportConstraints.maxHeight,
/// ),
/// child: IntrinsicHeight(
/// child: Column(
/// children: <Widget>[
/// Container(
/// // A fixed-height child.
/// color: Colors.yellow,
/// height: 120.0,
/// ),
/// Expanded(
/// // A flexible child that will grow to fit the viewport but
/// // still be at least as big as necessary to fit its contents.
/// child: Container(
/// color: Colors.blue,
/// height: 120.0,
/// ),
/// ),
/// ],
/// ),
/// ),
/// ),
/// );
/// },
/// )
/// ```
///
/// This technique is quite expensive, as it more or less requires that the contents /// This technique is quite expensive, as it more or less requires that the contents
/// of the viewport be laid out twice (once to find their intrinsic dimensions, and /// of the viewport be laid out twice (once to find their intrinsic dimensions, and
/// once to actually lay them out). The number of widgets within the column should /// once to actually lay them out). The number of widgets within the column should
...@@ -176,6 +147,46 @@ import 'scrollable.dart'; ...@@ -176,6 +147,46 @@ import 'scrollable.dart';
/// so that the intrinsic sizing algorithm can short-circuit the computation when it /// so that the intrinsic sizing algorithm can short-circuit the computation when it
/// reaches those parts of the subtree. /// reaches those parts of the subtree.
/// ///
/// {@tool snippet --template=stateless_widget}
/// In this example, the column becomes either as big as viewport, or as big as
/// the contents, whichever is biggest.
///
/// ```dart
/// Widget build(BuildContext context) {
/// return LayoutBuilder(
/// builder: (BuildContext context, BoxConstraints viewportConstraints) {
/// return SingleChildScrollView(
/// child: ConstrainedBox(
/// constraints: BoxConstraints(
/// minHeight: viewportConstraints.maxHeight,
/// ),
/// child: IntrinsicHeight(
/// child: Column(
/// children: <Widget>[
/// Container(
/// // A fixed-height child.
/// color: const Color(0xff808000), // Yellow
/// height: 120.0,
/// ),
/// Expanded(
/// // A flexible child that will grow to fit the viewport but
/// // still be at least as big as necessary to fit its contents.
/// child: Container(
/// color: const Color(0xff800000), // Red
/// height: 120.0,
/// ),
/// ),
/// ],
/// ),
/// ),
/// ),
/// );
/// },
/// );
/// }
/// ```
/// {@end-tool}
///
/// See also: /// See also:
/// ///
/// * [ListView], which handles multiple children in a scrolling list. /// * [ListView], which handles multiple children in a scrolling list.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment