diff --git a/dev/bots/test.dart b/dev/bots/test.dart index 39733764c0..a76a0f20fe 100644 --- a/dev/bots/test.dart +++ b/dev/bots/test.dart @@ -674,6 +674,7 @@ Future _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); } diff --git a/dev/snippets/README.md b/dev/snippets/README.md deleted file mode 100644 index c46531d8d4..0000000000 --- a/dev/snippets/README.md +++ /dev/null @@ -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. diff --git a/dev/snippets/assets/code_sample.png b/dev/snippets/assets/code_sample.png deleted file mode 100644 index 96bf7e86bf..0000000000 Binary files a/dev/snippets/assets/code_sample.png and /dev/null differ diff --git a/dev/snippets/assets/code_snippet.png b/dev/snippets/assets/code_snippet.png deleted file mode 100644 index cc3e125cc5..0000000000 Binary files a/dev/snippets/assets/code_snippet.png and /dev/null differ diff --git a/examples/api/README.md b/examples/api/README.md index b0e6ca1b5b..2c1a878295 100644 --- a/examples/api/README.md +++ b/examples/api/README.md @@ -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) diff --git a/examples/api/test/widgets/layout_builder/layout_builder.0_test.dart b/examples/api/test/widgets/layout_builder/layout_builder.0_test.dart new file mode 100644 index 0000000000..51c0ac6af8 --- /dev/null +++ b/examples/api/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)); + }); +}