# Metro Live

<!-- uncertain: newly created translation; needs full human review -->

<div id="introduction"></div>

## Introdução

<div class="ny-doc-strip">
<span class="ny-doc-strip-label">Requer</span>
<span class="ny-doc-strip-item">Nylo 7.2.0+</span>
<span class="ny-doc-strip-dot"></span>
<span class="ny-doc-strip-item">nylo_support 7.30.0+</span>
</div>

O **Nylo Live** permite que o Metro alcance seu app enquanto ele está rodando em modo debug (ou profile), através do Dart VM service. Ele encontra o app através do Dart Tooling Daemon sem nenhuma configuração, e só se conecta a apps construídos a partir do projeto atual.

O Nylo Live vem ativado por padrão em builds debug e profile; um build release não registra nada. Comandos que apenas leem o app (`live:status`, `live:run data`, `live:run routes`, ...) funcionam em builds profile também. Comandos que alteram o app (`route`, `storage`, `auth`, seeders, ...) precisam de um build debug.

Desative-o você mesmo, antes de qualquer outra coisa rodar, a partir de um provider:

``` dart
class AppProvider implements NyProvider {
  @override
  setup(Nylo nylo) async {
    nylo.useLive(false);
    return nylo;
  }

  @override
  boot(Nylo nylo) async {}
}
```

<div id="metro-live-shell"></div>

## O Shell metro live

<div class="ny-doc-strip">
<span class="ny-doc-strip-label">Requer</span>
<span class="ny-doc-strip-item">Nylo 7.2.0+</span>
<span class="ny-doc-strip-dot"></span>
<span class="ny-doc-strip-item">nylo_support 7.30.0+</span>
</div>

Abra um shell conectado ao seu app em execução:

``` bash
metro live
```

O Nylo Live descobre os apps em execução do projeto e se conecta a um -- ele pede para você escolher quando há mais de um em execução. Uma vez conectado, o prompt mostra o dispositivo e a rota atualmente em tela:

``` plaintext
Nylo Live · my_app
Connected to iPhone 15 Pro (debug)
Type help to see commands, and exit to leave.
iPhone 15 Pro /home ›
```

Comandos live derrubam o prefixo `metro live:` dentro do shell -- `status` em vez de `metro live:status`, `route /profile` em vez de `metro live:run route /profile`. Alguns comandos só rodam dentro do shell: `seed`, `seed:rollback`, `export`, `reload` e `restart`. Digite `help` para listar todos os comandos, ou `help <command>` para ver as opções de um comando. Digite `exit` (ou Ctrl+D) para sair.

Se o app reiniciar enquanto você estiver conectado, o shell se reconecta sozinho.

<div id="shared-options"></div>

### Opções Compartilhadas

Todo comando live -- dentro do shell ou como `metro live:*` -- aceita as mesmas opções:

| Opção | Descrição |
|--------|-------------|
| `-d, --device <#\|name>` | Direcionar um app pelo seu número em `metro live:devices` ou pelo nome do dispositivo |
| `--all` | Rodar em todos os apps em execução deste projeto |
| `--json` | Imprimir JSON legível por máquina |
| `--uri` | Usar este endereço do VM service em vez de descobrir apps |
| `--timeout` | Segundos para aguardar cada app durante a descoberta (padrão `3`) |

Um valor de opção escrito `@path` é lido a partir desse arquivo -- útil para um payload `--data` grande:

``` bash
metro live:run route /profile --data @user.json
```

Comece o valor com `@@` quando ele realmente começar com `@`.

<div id="tab-completion-and-history"></div>

### Autocompletar e Histórico

Dentro do shell, Tab autocompleta nomes de comandos, opções, rotas, chaves de storage e Backpack, e nomes de seeders contra o app conectado. O histórico é mantido em `.dart_tool/nylo/live_history` e sobrevive entre sessões.

<div id="running-a-script"></div>

### Executando um Script

Direcione um arquivo para dentro do `metro live` para executá-lo como um script, um comando por linha, parando no primeiro comando que falhar:

``` bash
metro live < seeds/demo.live
```

Linhas em branco e linhas começando com `#` são ignoradas.

<div id="live-devices"></div>

## live:devices

<div class="ny-doc-strip">
<span class="ny-doc-strip-label">Requer</span>
<span class="ny-doc-strip-item">Nylo 7.2.0+</span>
<span class="ny-doc-strip-dot"></span>
<span class="ny-doc-strip-item">nylo_support 7.30.0+</span>
</div>

Lista os apps em execução deste projeto, numerados para `-d`:

``` bash
metro live:devices
```
``` plaintext
Running apps for my_app:
#  DEVICE          PLATFORM  MODE   ROUTE   APP
1  iPhone 15 Pro   ios       debug  /home   my_app
2  Pixel 8         android   debug  /login  my_app
```

<div id="live-status"></div>

## live:status

<div class="ny-doc-strip">
<span class="ny-doc-strip-label">Requer</span>
<span class="ny-doc-strip-item">Nylo 7.2.0+</span>
<span class="ny-doc-strip-dot"></span>
<span class="ny-doc-strip-item">nylo_support 7.30.0+</span>
</div>

Mostra o app, dispositivo, modo de build, rota e pilha atuais, locale, tema, e se um usuário está autenticado:

``` bash
metro live:status
```
``` plaintext
App            my_app (development)
Device         iPhone 15 Pro · ios 17.4
Mode           debug
Route          /home
Stack          /home
Locale         en
Theme          default
Authenticated  yes
Live commands  cart:seed
Seeders        demo_user
```

<div id="live-run"></div>

## live:run

<div class="ny-doc-strip">
<span class="ny-doc-strip-label">Requer</span>
<span class="ny-doc-strip-item">Nylo 7.2.0+</span>
<span class="ny-doc-strip-dot"></span>
<span class="ny-doc-strip-item">nylo_support 7.30.0+</span>
</div>

Executa um comando embutido que inspeciona ou controla o app:

``` bash
metro live:run <command> [arguments]
```

| Comando | Descrição |
|---------|-------------|
| `data` | Mostrar os dados e campos da página em tela |
| `routes` | Listar as rotas registradas |
| `route` | Abrir uma página, com `--data` opcional |
| `back` | Voltar uma página, ou voltar para uma rota |
| `deeplink` | Abrir um deep link, ou mostrar como eles são configurados |
| `storage` | Listar o storage local, ou mostrar, salvar ou deletar um valor |
| `storage:clear` | Limpar o storage local, com `--keep` opcional |
| `backpack` | Listar valores do Backpack, ou mostrar, salvar ou deletar um |
| `auth` | Mostrar quem está autenticado, ou autenticar e desautenticar |
| `locale` | Trocar o idioma do app |
| `theme` | Trocar o tema do app |
| `state` | Enviar dados para um estado com `updateState` |
| `event` | Disparar um dos seus eventos |
| `toast` | Mostrar uma notificação toast |

Dentro do `metro live`, remova a parte `live:run` -- `route /profile` em vez de `metro live:run route /profile`:

``` bash
metro live:run route /profile --data '{"id": 42}'
metro live:run storage SK_COINS 10
metro live:run backpack auth_user
metro live:run auth login --data '{"id": 42}'
metro live:run deeplink myapp://product/42
metro live:run state HomePage --data '{"count": 2}'
metro live:run event LogoutEvent --data '{"reason": "test"}'
metro live:run toast "Item saved" --title Success
```

`data` também lê os campos que o estado da sua página declara, direto do app em execução -- não há reflection no Flutter, então isso só funciona para um estado que está de fato em tela:

``` bash
metro live:run data HomePage
```

<div id="live-commands"></div>

## Comandos Live

<div class="ny-doc-strip">
<span class="ny-doc-strip-label">Requer</span>
<span class="ny-doc-strip-item">Nylo 7.2.0+</span>
<span class="ny-doc-strip-dot"></span>
<span class="ny-doc-strip-item">nylo_support 7.30.0+</span>
</div>

Registre seus próprios comandos para rodar dentro do app, ao lado dos comandos embutidos acima.

<div id="creating-a-live-command"></div>

### Criando um Live Command

``` bash
metro make:command seed_cart --live
```

| Opção | Descrição |
|--------|-------------|
| `--category`, `-c` | A categoria do comando (padrão: `app`) |
| `--description` | Uma descrição de uma linha exibida pelo `--help` do comando |
| `--force`, `-f` | Cria o arquivo mesmo que já exista |

Isso cria um `LiveCommand` em `lib/app/commands/`, marca-o como `"type": "live"` em `commands.json`, o registra em `lib/bootstrap/live_commands.dart`, e conecta `liveCommands: liveCommands` em `nylo.configure(...)` em `app_provider.dart`:

``` dart
class SeedCartCommand extends LiveCommand {
  @override
  CommandBuilder builder(CommandBuilder command) {
    command.addOption('count', abbr: 'c', defaultValue: '3');
    command.addFlag('open', help: 'Open the cart afterwards');
    return command;
  }

  @override
  Future<void> handle(CommandResult result) async {
    final int count = result.getInt('count') ?? 3;
    await CartItem.seed(count: count);
    success('Seeded $count cart items');
  }
}
```

`LiveCommand` compartilha o formato `builder`/`handle` com os próprios comandos do Metro, mas `handle` roda **dentro do app**, então pode usar `NyStorage`, `routeTo`, `Auth` e seus models. `package:nylo_framework/live.dart` exporta `LiveCommand` junto com `Seeder`, `StorageSnapshot`, `LiveException` e `LiveOutput`.

<div id="running-a-live-command"></div>

### Executando um Live Command

Um novo live command precisa de um hot restart antes que o app em execução possa rodá-lo. Depois, a partir do terminal:

``` bash
metro app:seed_cart --count 5
```

<div id="seeders"></div>

## Seeders

<div class="ny-doc-strip">
<span class="ny-doc-strip-label">Requer</span>
<span class="ny-doc-strip-item">Nylo 7.2.0+</span>
<span class="ny-doc-strip-dot"></span>
<span class="ny-doc-strip-item">nylo_support 7.30.0+</span>
</div>

Um `Seeder` coloca seu app em execução em um estado conhecido -- autenticado, onboarding concluído, registros de exemplo -- e o traz de volta ao estado original.

<div id="creating-a-seeder"></div>

### Criando um Seeder

``` bash
metro make:seeder demo_user
```

| Opção | Descrição |
|--------|-------------|
| `--description` | Uma descrição de uma linha, exibida quando `seed` lista os seeders |
| `--force`, `-f` | Cria o arquivo mesmo que já exista |

Isso cria um `Seeder` em `lib/app/seeders/`, o registra em `lib/bootstrap/seeders.dart`, e conecta `seeders: seeders` em `nylo.configure(...)`:

``` dart
class DemoUserSeeder extends Seeder {
  @override
  String get description => 'Jane Doe, signed in, onboarding done';

  @override
  Future<void> up() async {
    await Auth.authenticate(data: {'name': 'Jane Doe', 'token': 'demo'});
    await saveToStorage({'onboarding_complete': true});
    success('Signed in as Jane Doe');
  }

  @override
  Future<void> down() async {
    await restore();
  }
}
```

Todo valor de storage e Backpack que `up()` altera é registrado automaticamente, então o `down()` padrão (`restore()`) devolve cada um exatamente como estava, na ordem inversa -- mesmo depois de um hot restart. Chame `seed(otherSeeders)` de dentro de um seeder para rodar outros como parte dele; as alterações deles também são registradas e revertidas.

<div id="seeding-and-rolling-back"></div>

### Seeding e Rollback

Seeding, rollback e exportação só rodam dentro do shell `metro live`:

``` plaintext
› seed demo_user
Seeding demo_user
  + storage    SK_ONBOARDING  added
  ✓ Signed in as Jane Doe
✓ Seeded demo_user in 42ms
Undo with seed:rollback demo_user

› seed:rollback demo_user
Rolling back demo_user
  - storage    SK_ONBOARDING  removed
✓ Rolled back demo_user in 8ms
```

`seed` sozinho lista os seeders registrados. `--fresh` limpa o storage e o Backpack antes de fazer o seed, e `--restart` reinicia o app com hot restart em seguida, para que ele já suba com os dados semeados:

``` plaintext
› seed demo_user --fresh --restart
```

<div id="storage-snapshots"></div>

## Snapshots de Armazenamento

<div class="ny-doc-strip">
<span class="ny-doc-strip-label">Requer</span>
<span class="ny-doc-strip-item">Nylo 7.2.0+</span>
<span class="ny-doc-strip-dot"></span>
<span class="ny-doc-strip-item">nylo_support 7.30.0+</span>
</div>

`export` salva tudo o que o app atualmente mantém em storage e Backpack -- como um seeder, ou como um arquivo JSON portátil -- e `seed <file>` carrega de volta.

<div id="exporting-a-snapshot"></div>

### Exportando um Snapshot

Execute sem nome para pré-visualizar o que seria capturado:

``` plaintext
› export
Storage on iPhone 15 Pro (2 values, 96 B)
KEY          TYPE    VALUE
SK_THEME     string  dark
SK_USER      model   {id: 1, name: Jane Doe}

Backpack (0 values)
Backpack is empty

Save it as a seeder with export <name>, or as a file with export --to <path>.json
```

Dê um nome para salvá-lo como um seeder em `lib/app/seeders/` (o mesmo que `make:seeder`, mas já preenchido com os valores capturados):

``` plaintext
› export pro_user
```

Ou grave em um arquivo:

``` plaintext
› export pro_user --to snapshots/pro_user.json
```

| Opção | Descrição |
|--------|-------------|
| `--to <path>` | Grava o snapshot neste arquivo JSON em vez de um seeder |
| `--description` | Uma descrição de uma linha para o seeder |
| `--only <keys>` | Chaves separadas por vírgula para exportar; `*` corresponde a qualquer coisa, ex: `--only SK_USER,onboarding_*` |
| `--except <keys>` | Chaves separadas por vírgula para deixar de fora, ex: `--except 'cache_*'` |
| `--backpack` / `--no-backpack` | Incluir valores do Backpack (padrão: incluído) |
| `--force`, `-f` | Substitui um seeder ou arquivo que já existe |

Um `Model`, ou qualquer classe com um decoder registrado, é marcado pelo nome da classe para que volte através do seu próprio decoder em vez de como um mapa simples. `export` avisa quando o snapshot contém chaves que parecem credenciais, e quando ele é grande o suficiente para que você provavelmente queira usar `--except`.

<div id="loading-a-snapshot"></div>

### Carregando um Snapshot

``` plaintext
› seed snapshots/pro_user.json
```

Carregar um arquivo é registrado da mesma forma que um seeder, então `seed:rollback <name>` (o nome do arquivo, ou o que `--as` tiver definido) também desfaz isso. A partir do código, um seeder pode aplicar um snapshot capturado com `importSnapshot`:

``` dart
class ProUserSeeder extends Seeder {
  @override
  Future<void> up() async {
    await importSnapshot(snapshot);
  }

  static const Map<String, Object?> snapshot = {
    'storage': {
      'SK_USER': {'type': 'model', 'model': 'User', 'value': {'id': 1}},
    },
    'backpack': {},
  };
}
```

`export` grava exatamente neste formato, então um arquivo gravado com `--to` pode ser colado diretamente no campo `snapshot` de um seeder.

<div id="page-actions"></div>

## Ações de Página

<div class="ny-doc-strip">
<span class="ny-doc-strip-label">Requer</span>
<span class="ny-doc-strip-item">Nylo 7.2.0+</span>
<span class="ny-doc-strip-dot"></span>
<span class="ny-doc-strip-item">nylo_support 7.30.0+</span>
</div>

`path.actions` em uma página te dá um `PageStateActions` para chamar essa página de fora dela, sem construir uma chamada `stateAction(...)` manualmente:

``` dart
class ProductPage extends NyStatefulWidget {
  static RouteView path = ("/product", (_) => ProductPage());
  static final actions = path.actions;

  ProductPage({super.key}) : super(child: () => _ProductPageState());
}

ProductPage.actions.showToast("Added to bag");
ProductPage.actions.refreshPage();
```

Veja [Chamando as Ações de uma Página](/docs/7.x/state-management#page-actions-shortcut) para o conjunto completo de ações integradas e como declarar suas próprias ações tipadas. O Nylo Live lê e usa as mesmas ações: `metro live:run data` lista o que uma página em tela expõe em `actions`, e `metro live:run state` envia dados para uma página diretamente pelo nome do seu estado.
