mirror of
https://github.com/flutter/flutter
synced 2024-08-27 12:00:58 +00:00
Add example test, update example READMEs (#91130)
This commit is contained in:
parent
2bab6514b0
commit
983cbb273b
|
@ -674,6 +674,7 @@ Future<void> _runFrameworkTests() async {
|
|||
workingDirectory: path.join(flutterRoot, 'examples', 'api'),
|
||||
);
|
||||
}
|
||||
await _runFlutterTest(path.join(flutterRoot, 'examples', 'api'), options: soundNullSafetyOptions);
|
||||
await _runFlutterTest(path.join(flutterRoot, 'examples', 'hello_world'), options: soundNullSafetyOptions);
|
||||
await _runFlutterTest(path.join(flutterRoot, 'examples', 'layers'), options: soundNullSafetyOptions);
|
||||
}
|
||||
|
|
|
@ -1,238 +0,0 @@
|
|||
# Dartdoc Generation
|
||||
|
||||
The Flutter API documentation contains code blocks that help provide context or
|
||||
a good starting point when learning to use any of Flutter's APIs.
|
||||
|
||||
To generate these code blocks, Flutter uses dartdoc tools to turn documentation
|
||||
in the source code into API documentation, as seen on https://api.flutter.dev/.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Types of code blocks](#types-of-code-blocks)
|
||||
- [Snippet tool](#snippet-tool)
|
||||
- [Sample tool](#sample-tool)
|
||||
- [Skeletons](#skeletons)
|
||||
- [Test Doc Generation Workflow](#test-doc-generation-workflow)
|
||||
|
||||
## Types of code blocks
|
||||
|
||||
There are three kinds of code blocks.
|
||||
|
||||
* A `snippet`, which is a more or less context-free code snippet that we
|
||||
magically determine how to analyze.
|
||||
|
||||
* A `dartpad` sample, which gets placed into a full-fledged application, and can
|
||||
be executed inline in the documentation on the web page using
|
||||
DartPad.
|
||||
|
||||
* A `sample`, which gets placed into a full-fledged application, but isn't
|
||||
placed into DartPad in the documentation because it doesn't make sense to do
|
||||
so.
|
||||
|
||||
Ideally, every sample is a DartPad sample, but some samples don't have any visual
|
||||
representation and some just don't make sense that way (for example, sample
|
||||
code for setting the system UI's notification area color on Android won't do
|
||||
anything on the web).
|
||||
|
||||
### Snippet Tool
|
||||
|
||||
![Code snippet image](assets/code_snippet.png)
|
||||
|
||||
The code `snippet` tool generates a block containing a description and example
|
||||
code. Here is an example of the code `snippet` tool in use:
|
||||
|
||||
```dart
|
||||
/// {@tool snippet}
|
||||
///
|
||||
/// If the avatar is to have an image, the image should be specified in the
|
||||
/// [backgroundImage] property:
|
||||
///
|
||||
/// ```dart
|
||||
/// CircleAvatar(
|
||||
/// backgroundImage: NetworkImage(userAvatarUrl),
|
||||
/// )
|
||||
/// ```
|
||||
/// {@end-tool}
|
||||
```
|
||||
|
||||
This will generate sample code that can be copied to the clipboard and added to
|
||||
existing applications.
|
||||
|
||||
This uses the skeleton for [snippet](config/skeletons/snippet.html) snippets
|
||||
when generating the HTML to put into the Dart docs.
|
||||
|
||||
#### Analysis
|
||||
|
||||
The `../bots/analyze_sample_code.dart` script finds code inside the `@tool
|
||||
snippet` sections and uses the Dart analyzer to check them.
|
||||
|
||||
There are several kinds of sample code you can specify:
|
||||
|
||||
* Constructor calls, typically showing what might exist in a build method. These
|
||||
will be inserted into an assignment expression assigning to a variable of type
|
||||
"dynamic" and followed by a semicolon, for analysis.
|
||||
|
||||
* Class definitions. These start with "class", and are analyzed verbatim.
|
||||
|
||||
* Other code. It gets included verbatim, though any line that says `// ...` is
|
||||
considered to separate the block into multiple blocks to be processed
|
||||
individually.
|
||||
|
||||
The above means that it's tricky to include verbatim imperative code (e.g. a
|
||||
call to a method) since it won't be valid to have such code at the top level.
|
||||
Instead, wrap it in a function or even a whole class, or make it a valid
|
||||
variable declaration.
|
||||
|
||||
You can declare code that should be included in the analysis but not shown in
|
||||
the API docs by adding a comment "// Examples can assume:" to the file (usually
|
||||
at the top of the file, after the imports), following by one or more
|
||||
commented-out lines of code. That code is included verbatim in the analysis. For
|
||||
example:
|
||||
|
||||
```dart
|
||||
// Examples can assume:
|
||||
// final BuildContext context;
|
||||
// final String userAvatarUrl;
|
||||
```
|
||||
|
||||
You can assume that the entire Flutter framework and most common
|
||||
`dart:*` packages are imported and in scope; `dart:math` as `math` and
|
||||
`dart:ui` as `ui`.
|
||||
|
||||
### Sample Tool
|
||||
|
||||
![Code sample image](assets/code_sample.png)
|
||||
|
||||
The code `sample` and `dartpad` tools can expand sample code into full Flutter
|
||||
applications. These sample applications can be directly copied and used to
|
||||
demonstrate the API's functionality in a sample application, or used with the
|
||||
`flutter create` command to create a local project with the sample code. The
|
||||
`dartpad` samples are embedded into the API docs web page and are live
|
||||
applications in the API documentation.
|
||||
|
||||
```dart
|
||||
/// {@tool sample --template=stateless_widget_material}
|
||||
/// This example shows how to make a simple [FloatingActionButton] in a
|
||||
/// [Scaffold], with a pink [backgroundColor] and a thumbs up [Icon].
|
||||
///
|
||||
/// ```dart
|
||||
/// Widget build(BuildContext context) {
|
||||
/// return Scaffold(
|
||||
/// appBar: AppBar(
|
||||
/// title: Text('Floating Action Button Sample'),
|
||||
/// ),
|
||||
/// body: Center(
|
||||
/// child: Text('Press the button below!')
|
||||
/// ),
|
||||
/// floatingActionButton: FloatingActionButton(
|
||||
/// onPressed: () {
|
||||
/// // Add your onPressed code here!
|
||||
/// },
|
||||
/// child: Icon(Icons.thumb_up),
|
||||
/// backgroundColor: Colors.pink,
|
||||
/// ),
|
||||
/// );
|
||||
/// }
|
||||
/// ```
|
||||
/// {@end-tool}
|
||||
```
|
||||
|
||||
This uses the skeleton for [application](config/skeletons/sample.html)
|
||||
snippets.
|
||||
|
||||
The `sample` and `dartpad` tools also allow for quick Flutter app generation
|
||||
using the following command:
|
||||
|
||||
```bash
|
||||
flutter create --sample=[directory.File.sampleNumber] [name_of_project_directory]
|
||||
```
|
||||
|
||||
This command is displayed as part of the sample in the API docs.
|
||||
|
||||
#### Templates
|
||||
|
||||
To support showing an entire app when you click on the right tab of the
|
||||
code sample UI, we have to be able to insert the `sample` or `dartpad` block
|
||||
into the template and instantiate the right parts.
|
||||
|
||||
To do this, there is a [config/templates](config/templates) directory that
|
||||
contains a list of templates. These templates represent an entire app that the
|
||||
`sample` or `dartpad` can be placed into, basically a replacement for
|
||||
`lib/main.dart` in a flutter app package.
|
||||
|
||||
For more information about how to create, use, or update templates, see
|
||||
[config/templates/README.md](config/templates/README.md).
|
||||
|
||||
#### Analysis
|
||||
|
||||
The `../bots/analyze_sample_code.dart` script finds code inside the `@tool
|
||||
sample` sections and uses the Dart analyzer to check them after applying the
|
||||
specified template.
|
||||
|
||||
## Skeletons
|
||||
|
||||
A skeleton (concerning this tool) is an HTML template into which the Dart
|
||||
code blocks and descriptions are interpolated.
|
||||
|
||||
There is currently one skeleton for
|
||||
[application](config/skeletons/sample.html) samples, one for
|
||||
[dartpad](config/skeletons/dartpad-sample.html), and one for
|
||||
[snippet](config/skeletons/snippet.html) code samples, but there could be more.
|
||||
|
||||
Skeletons use mustache notation (e.g. `{{code}}`) to mark where components will
|
||||
be interpolated into the template. It doesn't use the mustache
|
||||
package since these are simple string substitutions, but it uses the same
|
||||
syntax.
|
||||
|
||||
The code block generation tools that process the source input and emit HTML for
|
||||
output, which dartdoc places back into the documentation. Any options given to
|
||||
the `{@tool ...}` directive are passed on verbatim to the tool.
|
||||
|
||||
The `snippets` tool renders these examples through a combination of markdown
|
||||
and HTML using the `{@inject-html}` dartdoc directive.
|
||||
|
||||
## Test Doc Generation Workflow
|
||||
|
||||
If you are making changes to an existing code block or are creating a new code
|
||||
block, follow these steps to generate a local copy of the API docs and verify
|
||||
that your code blocks are showing up correctly:
|
||||
|
||||
1. Make an update to a code block or create a new code block.
|
||||
2. From the root directory, run `./dev/bots/docs.sh`. This should start
|
||||
generating a local copy of the API documentation.
|
||||
3. Once complete, check `./dev/docs/doc` to check your API documentation. The
|
||||
search bar will not work locally, so open `./dev/docs/doc/index.html` to
|
||||
navigate through the documentation, or search `./dev/docs/doc/flutter` for
|
||||
your page of interest.
|
||||
|
||||
Note that generating the sample output will not allow you to run your code in
|
||||
DartPad, because DartPad pulls the code it runs from the appropriate docs server
|
||||
(master or stable).
|
||||
|
||||
Copy the generated code and paste it into a regular DartPad instance to test if
|
||||
it runs in DartPad. To get the code that will be produced by your documentation
|
||||
changes, run sample analysis locally (see the next section) and paste the output
|
||||
into a DartPad at https://dartpad.dartlang.org.
|
||||
|
||||
## Running sample analysis locally
|
||||
|
||||
If all you want to do is analyze the sample code you have written locally, then
|
||||
generating the entire docs output takes a long time.
|
||||
|
||||
Instead, you can run the analysis locally with this command from the Flutter root:
|
||||
|
||||
```
|
||||
TMPDIR=/tmp bin/cache/dart-sdk/bin/dart dev/bots/analyze_sample_code.dart --temp=samples
|
||||
```
|
||||
|
||||
This will analyze the samples, and leave the generated files in `/tmp/samples`
|
||||
|
||||
You can find the sample you are working on in `/tmp/samples`. It is named using the
|
||||
path to the file it is in, and the line of the file that the `{@tool ...}` directive
|
||||
is on.
|
||||
|
||||
For example, the file `sample.src.widgets.animated_list.52.dart` points to the sample
|
||||
in `packages/flutter/src/widgets/animated_list.dart` at line 52. You can then take the
|
||||
contents of that file, and paste it into [Dartpad](https://dartpad.dev) and see if it
|
||||
works. If the sample relies on new features that have just landed, it may not work
|
||||
until the features make it into the `dev` branch.
|
Binary file not shown.
Before Width: | Height: | Size: 111 KiB |
Binary file not shown.
Before Width: | Height: | Size: 38 KiB |
|
@ -1,67 +1,135 @@
|
|||
# API Example Code
|
||||
|
||||
This directory contains the API sample code that is referenced from the
|
||||
API documentation in each class.
|
||||
This directory contains the API sample code that is referenced from the API
|
||||
documentation in the framework.
|
||||
|
||||
They can be run individually by just specifying the path to the example on the
|
||||
command line (or in the run configuration of an IDE).
|
||||
The examples can be run individually by just specifying the path to the example
|
||||
on the command line (or in the run configuration of an IDE).
|
||||
|
||||
For example, to run, in Chrome, the first example from the `Curve2D` class, you
|
||||
would run it like so, from this [api](.) directory:
|
||||
For example (no pun intended!), to run, the first example from the `Curve2D`
|
||||
class in Chrome, you would run it like so from the [api](.) directory:
|
||||
|
||||
```
|
||||
% flutter run -d chrome lib/animation/curves/curve2_d.0.dart
|
||||
```
|
||||
|
||||
These same examples are available on the API docs site. For instance, the
|
||||
example above is available on [this
|
||||
page](https://api.flutter.dev/flutter/animation/Curve2D-class.html#animation.Curve2D.1).
|
||||
Most of them are available as interactive examples in Dartpad, but some just
|
||||
don't make sense on the web, and so are available as standalone examples that
|
||||
can be run here.
|
||||
All of these same examples are available on the API docs site. For instance, the
|
||||
example above is available on [this page](
|
||||
https://api.flutter.dev/flutter/animation/Curve2D-class.html#animation.Curve2D.1).
|
||||
Most of the samples are available as interactive examples in
|
||||
[Dartpad](https://dartpad.dev), but some (the ones marked with `{@tool sample}`
|
||||
in the framework source code), just don't make sense on the web, and so are
|
||||
available as standalone examples that can be run here. For instance, setting the
|
||||
system overlay style doesn't make sense on the web (it only changes the
|
||||
notification area background color on Android), so you can run the example for
|
||||
that on an Android device like so:
|
||||
|
||||
```
|
||||
% flutter run -d MyAndroidDevice lib/services/system_chrome/system_chrome.set_system_u_i_overlay_style.1.dart
|
||||
```
|
||||
|
||||
## Naming
|
||||
|
||||
The naming scheme for the files is similar to the hierarchy under
|
||||
[packages/flutter/lib/src](../../packages/flutter/lib/src), except that the
|
||||
files are represented as directories, and each sample in each file as a separate
|
||||
file in that directory. So, for the example above, the examples are from the
|
||||
files are represented as directories (without the `.dart` suffix), and each
|
||||
sample in the file is a separate file in that directory. So, for the example
|
||||
above, where the examples are from the
|
||||
[packages/flutter/lib/src/animation/curves.dart](../../packages/flutter/lib/src/animation/curves.dart)
|
||||
file, the `Curve2D` class, and the first sample (hence the index "0") for that
|
||||
symbol resides in the
|
||||
[lib/animation/curves/curve2_d.0.dart](lib/animation/curves/curve2_d.0.dart)
|
||||
file.
|
||||
file, the `Curve2D` class, the first sample (hence the index "0") for that
|
||||
symbol resides in the file named
|
||||
[lib/animation/curves/curve2_d.0.dart](lib/animation/curves/curve2_d.0.dart).
|
||||
|
||||
Symbol names are converted from "CamelCase" to "snake_case". Dots are left
|
||||
between symbol names, so the first example for symbol
|
||||
`InputDecoration.prefixIconConstraints` would be converted to
|
||||
`input_decoration.prefix_icon_constraints.0.dart`.
|
||||
|
||||
If the same example is linked to from multiple symbols, the source will be in
|
||||
the canonical location for one of the symbols, and the link in the API docs
|
||||
block for the other symbols will point to the first symbol's example location.
|
||||
|
||||
## Authoring
|
||||
|
||||
When authoring these examples, place a block like so in the Dartdoc
|
||||
documentation for the symbol you would like to attach it to. Here's what it
|
||||
might look like if you wanted to add a new example to the `Curve2D` class. First
|
||||
add the stub to the symbol documentation:
|
||||
> For more detailed information about authoring examples, see
|
||||
> [the snippets package](https://pub.dev/packages/snippets).
|
||||
|
||||
When authoring examples, first place a block in the Dartdoc documentation for
|
||||
the symbol you would like to attach it to. Here's what it might look like if you
|
||||
wanted to add a new example to the `Curve2D` class:
|
||||
|
||||
```dart
|
||||
/// {@tool dartpad --template=material_scaffold}
|
||||
/// {@tool dartpad}
|
||||
/// Write a description of the example here. This description will appear in the
|
||||
/// API web documentation to introduce the example.
|
||||
///
|
||||
/// ** See code in examples/api/lib/animation/curves/curve2_d.0.dart **
|
||||
/// {@end-tool}
|
||||
```
|
||||
|
||||
The "See code in" line needs to be formatted exactly as above, with no wrapping
|
||||
or newlines, one space after the "`**`" at the beginning, and one space before
|
||||
the "`**`" at the end, and the words "See code in" at the beginning of the line.
|
||||
This is what the snippets tool and the IDE use when finding the example source
|
||||
code that you are creating.
|
||||
|
||||
Use `{@tool dartpad}` for Dartpad examples, and use `{@tool sample}` for
|
||||
examples that shouldn't be run/shown in Dartpad.
|
||||
|
||||
Once that comment block is inserted in the source code, create a new file at the
|
||||
appropriate path under [`examples/api`](.). You should also add tests for your
|
||||
sample code under [`examples/api/test`](./test).
|
||||
|
||||
The entire example should be in a single file, so that Dartpad can load it.
|
||||
|
||||
Only packages that can be loaded by Dartpad may be imported. If you use one that
|
||||
hasn't been used in an example before, you may have to add it to the
|
||||
[pubspec.yaml](pubspec.yaml) in the api directory.
|
||||
|
||||
## Snippets
|
||||
|
||||
There is another type of example that can also be authored, using `{@tool
|
||||
snippet}`. Snippet examples are just written inline in the source, like so:
|
||||
|
||||
```dart
|
||||
/// {@tool dartpad}
|
||||
/// Write a description of the example here. This description will appear in the
|
||||
/// API web documentation to introduce the example.
|
||||
///
|
||||
/// ```dart
|
||||
/// // These are the sections you want to fill out in the template.
|
||||
/// // They will be transferred to the example file when you extract it.
|
||||
/// // Sample code goes here, e.g.:
|
||||
/// const Widget emptyBox = SizedBox();
|
||||
/// ```
|
||||
/// {@end-tool}
|
||||
```
|
||||
|
||||
Then install the `extract_sample` command with:
|
||||
The source for these snippets isn't stored under the [`examples/api`](.)
|
||||
directory, or available in Dartpad in the API docs, since they're not intended
|
||||
to be runnable, they just show some incomplete snippet of example code. It must
|
||||
compile (in the context of the sample analyzer), but doesn't need to do
|
||||
anything. See [the snippets documentation](
|
||||
https://pub.dev/packages/snippets#snippet-tool) for more information about the
|
||||
context that the analyzer uses.
|
||||
|
||||
```
|
||||
% pub global activate snippets
|
||||
```
|
||||
## Writing Tests
|
||||
|
||||
And run the `extract_sample` command from the Flutter repo dir:
|
||||
Examples are required to have tests. There is already a "smoke test" that runs
|
||||
all the API examples, just to make sure that they start up without crashing. In
|
||||
addition, we also require writing tests of functionality in the examples, and
|
||||
generally just do what we normally do for writing tests. The one thing that
|
||||
makes it more challenging for the examples is that they can't really be written
|
||||
for testability in any obvious way, since that would probably complicate the
|
||||
example and make it harder to explain.
|
||||
|
||||
```
|
||||
$ pub global run extract_sample packages/flutter/lib/src/animation/curves.dart
|
||||
```
|
||||
As an example, in regular framework code, you might include a parameter for a
|
||||
`Platform` object that can be overridden by a test to supply a dummy platform,
|
||||
but in the example, this would be unnecessary complexity for the example. In all
|
||||
other ways, these are just normal tests.
|
||||
|
||||
This will create a new file in the `examples/api` directory, in this case it
|
||||
would create `examples/api/lib/animation/curves/curve2_d.1.dart`
|
||||
Tests go into a directory under [test](./test) that matches their location under
|
||||
[lib](./lib). They are named the same as the example they are testing, with
|
||||
`_test.dart` at the end, like other tests. For instance, a `LayoutBuilder`
|
||||
example that resides in [`lib/widgets/layout_builder/layout_builder.0.dart`](
|
||||
./lib/widgets/layout_builder/layout_builder.0.dart) would have its tests in a
|
||||
file named [`test/animation/curves/curve2_d.0_test.dart`](
|
||||
./test/widgets/layout_builder/layout_builder.0_test.dart)
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_api_samples/widgets/layout_builder/layout_builder.0.dart' as example;
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('has two containers when wide', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
const example.MyApp(),
|
||||
);
|
||||
|
||||
final Finder containerFinder = find.byType(Container);
|
||||
expect(containerFinder, findsNWidgets(2));
|
||||
});
|
||||
testWidgets('has one container when narrow', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 400),
|
||||
child: const example.MyApp(),
|
||||
),
|
||||
);
|
||||
|
||||
final Finder containerFinder = find.byType(Container);
|
||||
expect(containerFinder, findsNWidgets(2));
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue