|
| 1 | +# Dartdoc Sample Generation |
| 2 | + |
| 3 | +The Flutter API documentation contains code blocks that help provide context or |
| 4 | +a good starting point when learning to use any of Flutter's APIs. |
| 5 | + |
| 6 | +To generate these code blocks, Flutter uses dartdoc tools to turn documentation |
| 7 | +in the source code into API documentation, as seen on https://api.flutter.dev/. |
| 8 | + |
| 9 | +## Table of Contents |
| 10 | + |
| 11 | +- [Types of code blocks](#types-of-code-blocks) |
| 12 | + - [Snippet tool](#snippet-tool) |
| 13 | + - [Sample tool](#sample-tool) |
| 14 | +- [Skeletons](#skeletons) |
| 15 | +- [Test Doc Generation Workflow](#test-doc-generation-workflow) |
| 16 | + |
| 17 | +## Types of code blocks |
| 18 | + |
| 19 | +There are three kinds of code blocks. |
| 20 | + |
| 21 | +* A `snippet`, which is a more or less context-free code snippet that we |
| 22 | + magically determine how to analyze. |
| 23 | + |
| 24 | +* A `dartpad` sample, which gets placed into a full-fledged application, and can |
| 25 | + be executed inline in the documentation on the web page using |
| 26 | + DartPad. |
| 27 | + |
| 28 | +* A `sample`, which gets placed into a full-fledged application, but isn't |
| 29 | + placed into DartPad in the documentation because it doesn't make sense to do |
| 30 | + so. |
| 31 | + |
| 32 | +Ideally, every sample is a DartPad sample, but some samples don't have any visual |
| 33 | +representation and some just don't make sense that way (for example, sample |
| 34 | +code for setting the system UI's notification area color on Android won't do |
| 35 | +anything on the web). |
| 36 | + |
| 37 | +### Snippet Tool |
| 38 | + |
| 39 | + |
| 40 | + |
| 41 | +The code `snippet` tool generates a block containing a description and example |
| 42 | +code. Here is an example of the code `snippet` tool in use: |
| 43 | + |
| 44 | +```dart |
| 45 | +/// {@tool snippet} |
| 46 | +/// |
| 47 | +/// If the avatar is to have an image, the image should be specified in the |
| 48 | +/// [backgroundImage] property: |
| 49 | +/// |
| 50 | +/// ```dart |
| 51 | +/// CircleAvatar( |
| 52 | +/// backgroundImage: NetworkImage(userAvatarUrl), |
| 53 | +/// ) |
| 54 | +/// ``` |
| 55 | +/// {@end-tool} |
| 56 | +``` |
| 57 | + |
| 58 | +This will generate sample code that can be copied to the clipboard and added to |
| 59 | +existing applications. |
| 60 | + |
| 61 | +This uses the skeleton for `snippet` snippets when generating the HTML to put |
| 62 | +into the Dart docs. You can find this [template in the Flutter |
| 63 | +repo](https://github.com/flutter/flutter/blob/master/dev/snippets/config/skeletons/snippet.html). |
| 64 | + |
| 65 | +#### Analysis |
| 66 | + |
| 67 | +The |
| 68 | +[`analyze_sample_code.dart`](https://github.com/flutter/flutter/blob/master/dev/bots/analyze_sample_code.dart) |
| 69 | +script finds code inside the `@tool |
| 70 | +snippet` sections and uses the Dart analyzer to check them. |
| 71 | + |
| 72 | +There are several kinds of sample code you can specify: |
| 73 | + |
| 74 | +* Constructor calls, typically showing what might exist in a build method. These |
| 75 | + will be inserted into an assignment expression assigning to a variable of type |
| 76 | + "dynamic" and followed by a semicolon, for analysis. |
| 77 | + |
| 78 | +* Class definitions. These start with "class", and are analyzed verbatim. |
| 79 | + |
| 80 | +* Other code. It gets included verbatim, though any line that says `// ...` is |
| 81 | + considered to separate the block into multiple blocks to be processed |
| 82 | + individually. |
| 83 | + |
| 84 | +The above means that it's tricky to include verbatim imperative code (e.g. a |
| 85 | +call to a method) since it won't be valid to have such code at the top level. |
| 86 | +Instead, wrap it in a function or even a whole class, or make it a valid |
| 87 | +variable declaration. |
| 88 | + |
| 89 | +You can declare code that should be included in the analysis but not shown in |
| 90 | +the API docs by adding a comment "// Examples can assume:" to the file (usually |
| 91 | +at the top of the file, after the imports), following by one or more |
| 92 | +commented-out lines of code. That code is included verbatim in the analysis. For |
| 93 | +example: |
| 94 | + |
| 95 | +```dart |
| 96 | +// Examples can assume: |
| 97 | +// final BuildContext context; |
| 98 | +// final String userAvatarUrl; |
| 99 | +``` |
| 100 | + |
| 101 | +You can assume that the entire Flutter framework and most common |
| 102 | +`dart:*` packages are imported and in scope; `dart:math` as `math` and |
| 103 | +`dart:ui` as `ui`. |
| 104 | + |
| 105 | +### Sample Tool |
| 106 | + |
| 107 | + |
| 108 | + |
| 109 | +The code `sample` and `dartpad` tools can expand sample code into full Flutter |
| 110 | +applications. These sample applications can be directly copied and used to |
| 111 | +demonstrate the API's functionality in a sample application, or used with the |
| 112 | +`flutter create` command to create a local project with the sample code. The |
| 113 | +`dartpad` samples are embedded into the API docs web page and are live |
| 114 | +applications in the API documentation. |
| 115 | + |
| 116 | +```dart |
| 117 | +/// {@tool sample --template=stateless_widget_material} |
| 118 | +/// This example shows how to make a simple [FloatingActionButton] in a |
| 119 | +/// [Scaffold], with a pink [backgroundColor] and a thumbs up [Icon]. |
| 120 | +/// |
| 121 | +/// ```dart |
| 122 | +/// Widget build(BuildContext context) { |
| 123 | +/// return Scaffold( |
| 124 | +/// appBar: AppBar( |
| 125 | +/// title: Text('Floating Action Button Sample'), |
| 126 | +/// ), |
| 127 | +/// body: Center( |
| 128 | +/// child: Text('Press the button below!') |
| 129 | +/// ), |
| 130 | +/// floatingActionButton: FloatingActionButton( |
| 131 | +/// onPressed: () { |
| 132 | +/// // Add your onPressed code here! |
| 133 | +/// }, |
| 134 | +/// child: Icon(Icons.thumb_up), |
| 135 | +/// backgroundColor: Colors.pink, |
| 136 | +/// ), |
| 137 | +/// ); |
| 138 | +/// } |
| 139 | +/// ``` |
| 140 | +/// {@end-tool} |
| 141 | +``` |
| 142 | + |
| 143 | +This uses the skeleton for [application](https://github.com/flutter/flutter/blob/master/dev/snippets/config/skeletons/sample.html) |
| 144 | +snippets in the Flutter repo. |
| 145 | + |
| 146 | +The `sample` and `dartpad` tools also allow for quick Flutter app generation |
| 147 | +using the following command: |
| 148 | + |
| 149 | +```bash |
| 150 | +flutter create --sample=[directory.File.sampleNumber] [name_of_project_directory] |
| 151 | +``` |
| 152 | + |
| 153 | +This command is displayed as part of the sample in the API docs. |
| 154 | + |
| 155 | +#### Templates |
| 156 | + |
| 157 | +To support showing an entire app when you click on the right tab of the |
| 158 | +code sample UI, we have to be able to insert the `sample` or `dartpad` block |
| 159 | +into the template and instantiate the right parts. |
| 160 | + |
| 161 | +To do this, there is a [templates |
| 162 | +directory](https://github.com/flutter/flutter/blob/master/dev/snippets/config/templates) |
| 163 | +that |
| 164 | +contains a list of templates. These templates represent an entire app that the |
| 165 | +`sample` or `dartpad` can be placed into, basically a replacement for |
| 166 | +`lib/main.dart` in a flutter app package. |
| 167 | + |
| 168 | +For more information about how to create, use, or update templates, see the |
| 169 | +[templates README.md](https://github.com/flutter/flutter/blob/master/dev/snippets/config/templates/README.md). |
| 170 | + |
| 171 | +#### Analysis |
| 172 | + |
| 173 | +The `../bots/analyze_sample_code.dart` script finds code inside the `@tool |
| 174 | +sample` sections and uses the Dart analyzer to check them after applying the |
| 175 | +specified template. |
| 176 | + |
| 177 | +## Skeletons |
| 178 | + |
| 179 | +A skeleton (concerning this tool) is an HTML template into which the Dart |
| 180 | +code blocks and descriptions are interpolated. |
| 181 | + |
| 182 | +There is currently one skeleton for |
| 183 | +[application](https://github.com/flutter/flutter/blob/master/dev/snippets/config/skeletons/sample.html) |
| 184 | +samples, one for |
| 185 | +[dartpad](https://github.com/flutter/flutter/blob/master/dev/snippets/config/skeletons/dartpad-sample.html), |
| 186 | +and one for |
| 187 | +[snippet](https://github.com/flutter/flutter/blob/master/dev/snippets/config/skeletons/snippet.html) |
| 188 | +code samples, but there could be more. |
| 189 | + |
| 190 | +Skeletons use mustache notation (e.g. `{{code}}`) to mark where components will |
| 191 | +be interpolated into the template. It doesn't use the mustache |
| 192 | +package since these are simple string substitutions, but it uses the same |
| 193 | +syntax. |
| 194 | + |
| 195 | +The code block generation tools that process the source input and emit HTML for |
| 196 | +output, which dartdoc places back into the documentation. Any options given to |
| 197 | +the `{@tool ...}` directive are passed on verbatim to the tool. |
| 198 | + |
| 199 | +The `snippets` tool renders these examples through a combination of markdown |
| 200 | +and HTML using the `{@inject-html}` dartdoc directive. |
| 201 | + |
| 202 | +## Test Doc Generation Workflow |
| 203 | + |
| 204 | +If you are making changes to an existing code block or are creating a new code |
| 205 | +block, follow these steps to generate a local copy of the API docs and verify |
| 206 | +that your code blocks are showing up correctly: |
| 207 | + |
| 208 | +1. Make an update to a code block or create a new code block. |
| 209 | +2. From the root directory, run `./dev/bots/docs.sh`. This should start |
| 210 | + generating a local copy of the API documentation. |
| 211 | +3. Once complete, check `./dev/docs/doc` to check your API documentation. The |
| 212 | + search bar will not work locally, so open `./dev/docs/doc/index.html` to |
| 213 | + navigate through the documentation, or search `./dev/docs/doc/flutter` for |
| 214 | + your page of interest. |
| 215 | + |
| 216 | +Note that generating the sample output will not allow you to run your code in |
| 217 | +DartPad, because DartPad pulls the code it runs from the appropriate docs server |
| 218 | +(master or stable). |
| 219 | + |
| 220 | +Copy the generated code and paste it into a regular DartPad instance to test if |
| 221 | +it runs in DartPad. To get the code that will be produced by your documentation |
| 222 | +changes, run sample analysis locally (see the next section) and paste the output |
| 223 | +into a DartPad at https://dartpad.dartlang.org. |
| 224 | + |
| 225 | +## Running sample analysis locally |
| 226 | + |
| 227 | +If all you want to do is analyze the sample code you have written locally, then |
| 228 | +generating the entire docs output takes a long time. |
| 229 | + |
| 230 | +Instead, you can run the analysis locally with this command from the Flutter root: |
| 231 | + |
| 232 | +``` |
| 233 | +TMPDIR=/tmp bin/cache/dart-sdk/bin/dart dev/bots/analyze_sample_code.dart --temp=samples |
| 234 | +``` |
| 235 | + |
| 236 | +This will analyze the samples, and leave the generated files in `/tmp/samples` |
| 237 | + |
| 238 | +You can find the sample you are working on in `/tmp/samples`. It is named using the |
| 239 | +path to the file it is in, and the line of the file that the `{@tool ...}` directive |
| 240 | +is on. |
| 241 | + |
| 242 | +For example, the file `sample.src.widgets.animated_list.52.dart` points to the sample |
| 243 | +in `packages/flutter/src/widgets/animated_list.dart` at line 52. You can then take the |
| 244 | +contents of that file, and paste it into [Dartpad](https://dartpad.dev) and see if it |
| 245 | +works. If the sample relies on new features that have just landed, it may not work |
| 246 | +until the features make it into the `dev` branch. |
0 commit comments