blob: 5a572cac9148b37a02fccb33720c7e89db0a75b1 [file] [log] [blame] [view]
# Flutter Module Development
TODO(fxbug.dev/15912): Update documentation below.
This directory demonstrates how you create modules with Dart and Flutter. At the
moment this document assumes that every module gets built as part of the core
fuchsia build and included in the bootfs.
# Example Modules
## Hello
(More samples located in `//topaz/examples/ui/`)
This example demonstrates how to create a minimal flutter module and implement
the `Module` interface. It shows a simple flutter text widget displaying "hello"
on the screen.
## Running the Examples on Fuchsia
You can run an example module without going through the full-blown session shell.
The available URLs for flutter module examples are:
* `hello_mod`
After a successful build of fuchsia, type the following command from the zx
console to run the basemgr with the dev session shell:
```sh
killall scenic # Kills all other mods.
basemgr --session_shell=dev_session_shell --session_shell_args=--root_module=hello_mod
```
# Basics
A flutter module is a flutter app that uses ModuleDriver.
Below we reproduce the contents of `main()` from that example:
```dart
final ModuleDriver _driver = ModuleDriver();
void main() {
setupLogger(name: 'Hello mod');
_driver.start().then((ModuleDriver driver) {
log.info('Hello mod started');
});
runApp(
MaterialApp(
title: 'Hello mod',
home: ScopedModel<_MyModel>(
model: _MyModel(),
child: _MyScaffold(),
),
),
);
}
```
# Importing Packages
## Adding Dependency to BUILD.gn
To import a dart package written within the fuchsia tree, the dependency should
be added to the project's `BUILD.gn`. The `BUILD.gn` file for the hello_mod
example looks like this:
```gn
import("//topaz/runtime/flutter_runner/flutter_app.gni")
flutter_app("hello_mod") {
main_dart = "main.dart"
package_name = "hello_mod"
fuchsia_package_name = "hello_mod"
deps = [
"//topaz/public/dart/widgets:lib.widgets",
"//topaz/public/lib/app_driver/dart",
]
}
```
There are two types of dart packages we can include as `BUILD.gn` dependencies.
### 1. Normal Dart Packages
Any third-party dart packages, or regular dart packages manually written in the
fuchsia tree. Import them with their relative paths from the `<fuchsia_root>`
directory followed by two slashes. Third-party dart packages are usually located
at `//third_party/dart-pkg/pub/<package_name>`.
### 2. FIDL-Generated Dart Bindings
To use any FIDL generated dart bindings, you need to first look at the
`BUILD.gn` defining the `fidl` target that contains the desired `.fidl` file.
For example, let's say we want to import and use the `module.fidl` file (located
in `//peridot/public/lib/module/fidl/`) in our dart code. We should first
look at the `BUILD.gn` file, in this case `//peridot/public/lib/BUILD.gn`. In
this file we can see that the `module.fidl` file is included in the
`fidl("fidl")` target.
```gn
fidl("fidl") {
sources = [
...
"module/fidl/module.fidl", # This is the fidl we want to use for now.
...
]
}
```
This means that we need to depend on this group of fidl files. In our module's
`BUILD.gn`, we can add the dependency with the following syntax:
`"//<dir>:<fidl_target_name>_dart"`
Once this is done, we can use all the protocols defined in `.fidl` files
contained in this `story` fidl target from our code.
## Importing in Dart Code
Once the desired package is added as a BUILD.gn dependency, the dart files in
those packages can be imported in our dart code. Importing dart packages in
fuchsia looks a bit different than normal dart packages. Let's look at the
import statements in `main.dart` of the hello_world example.
```dart
import 'package:lib.app.dart/app.dart';
import 'package:lib.app.fidl/service_provider.fidl.dart';
import 'package:apps.modular.services.story/link.fidl.dart';
import 'package:apps.modular.services.module/module.fidl.dart';
import 'package:apps.modular.services.module/module_context.fidl.dart';
import 'package:lib.fidl.dart/bindings.dart';
import 'package:flutter/widgets.dart';
```
To import things in the fuchsia tree, we use dots (`.`) instead of slashes (`/`)
as path delimiter. For FIDL-generated dart files, we add `.dart` at the end of
the corresponding fidl file path. (e.g. `module.fidl.dart`)
# Using FIDL Dart Bindings
See the [FIDL tutorial](/docs/development/languages/fidl/tutorials/dart) and
[bindings reference](/docs/reference/fidl/bindings/dart-bindings.md)
## Things to Watch Out For
### Handles Can Only Be Used Once
Once an `InterfaceHandle<Foo>` is bound to a proxy, the handle cannot be used in
other places. Often, in case you have to share the same service with multiple
parties (e.g. sharing the same `fuchsia::modular::Link` service across multiple
modules), the service will provide a way to obtain a duplicate handle (e.g.
`fuchsia::modular::Link::Dup()`).
You can also call `unbind()` method on `ProxyController` to get the usable
`InterfaceHandle<Foo>` back, which then can be used by someone else.
### Proxies and Bindings Should Be Closed Properly
You need to explicitly close `FooProxy` and `FooBinding` objects that are bound
to channels, when they are no longer in use. You do not need to explicitly close
`InterfaceRequest<Foo>` or `InterfaceHandle<Foo>` objects, as those objects
represent unbound channels.
If you don't close or unbind these objects and they get picked up by the garbage
collector, then FIDL will terminate the process and (in debug builds) log the
Dart stack for when the object was bound. The only exception to this rule is for
*static* objects that live as long as the isolate itself. The system is able to
close these objects automatically for you as part of an orderly shutdown of the
isolate.
If you are writing a Flutter widget, you can override the `dispose()` function
on `State` to get notified when you're no longer part of the tree. That's a
common time to close the proxies used by that object as they are often no longer
needed.
# Other Useful Tips
## Getting the Atom dartlang plugin to work correctly
You need to have the correct `.packages` file generated for the dart packages in
fuchsia tree. After building fuchsia, run this script form the terminal of your
development machine:
```sh
<fuchsia_root>$ scripts/symlink-dot-packages.py
```
Also, for flutter projects, the following line should be manually added to the
`.packages` file manually (fill in the fuchsia root dir of yours):
```
sky_engine:file:///<abs_fuchsia_root>/prebuilt/third_party/sky_engine/lib/
```
You might have to relaunch Atom to get everything working correctly. With this
`.packages` files, you get all dartanalyzer errors/warnings, jump to definition,
auto completion features.