Flutter: O estado da instância não é salvo quando o aplicativo é encerrado pelo sistema operacional

Criado em 12 nov. 2016  ·  139Comentários  ·  Fonte: flutter/flutter

O que é o estado da instância e por que existe

No Android, uma atividade pode ser eliminada a qualquer momento pelo sistema. Isso acontece geralmente quando o Android precisa de memória quando sua atividade não está em primeiro plano ou devido a uma alteração de configuração não tratada, como uma alteração de localidade.
Para evitar que o usuário tenha que reiniciar o que fez do zero quando o Android encerrou a atividade, o sistema chama onSaveInstanceState(…) quando a atividade é pausada, onde o aplicativo deve salvar seus dados em um Bundle , e passa o pacote salvo em onCreate(…) e onRestoreInstanceState(…) quando a tarefa é retomada se a atividade foi eliminada pelo sistema.

A questão sobre isso na vibração

Nos aplicativos de flutter que experimentei (Flutter Gallery e o projeto base com o contador de torneiras FAB), se eu abrir aplicativos suficientes para fazer o Android matar a atividade do aplicativo de flutter, todo o estado é perdido quando eu voltar para a atividade de flutter (enquanto não tendo remover a tarefa de recentes).

Passos para reproduzir

  1. Instale a Galeria Flutter
  2. Abra as configurações do dispositivo e, nas opções do desenvolvedor, ative "Não manter atividades". (veja a imagem)
    dev_option
    Isso permitirá simular quando o Android mata Activities porque falta memória e você não está em primeiro plano.
  3. Abra o aplicativo Flutter Gallery e vá para qualquer lugar que não seja a tela principal.
  4. Vá para o inicializador pressionando o botão inicial do dispositivo.
  5. Pressione o botão de visão geral e retorne à Galeria Flutter. Aqui está o bug.

O que é esperado: o aplicativo está no mesmo estado em que paramos, com a IU intacta.
O que acontece: a atividade é reiniciada do zero, perdendo todo o estado da IU, até mesmo formulários muito longos.

P2 annoyance crowd routes framework new feature

Comentários muito úteis

Atualização rápida: Atualmente, estou trabalhando ativamente na exploração de soluções para este problema.

Todos 139 comentários

https://github.com/flutter/flutter/issues/3427 também está provavelmente relacionado.

@eseidelGoogle Isso mesmo. Em qual formato o estado de atividade de flutter pode ser salvo, se é que pode ser como na versão atual?

No momento, não fazemos nada para salvar nada.

A estrutura em si tem muito pouco estado que valha a pena salvar - é tudo animação e coisas assim - então pode ser que sempre deixemos isso para o aplicativo fazer. Provavelmente deveríamos expô-lo no nível do Dart, no entanto. (No momento, ele está exposto apenas no nível Java.)

@Hixie No Android, todas as visualizações do framework têm seu estado salvo e restaurado automaticamente pelo sistema, o que requer apenas que o desenvolvedor salve manualmente a parte não UI do estado da instância. Não deve funcionar da mesma forma para todos os widgets de IU para evitar que os desenvolvedores tenham que escrever todo o clichê a cada vez para salvar onde o usuário estava, a posição de rolagem, o estado habilitado de algum botão, o estado da tela anterior e assim sobre…?

No Flutter, há muito pouco estado de estrutura para salvar. Por exemplo, o estado habilitado de um botão não é um estado, é uma entrada fornecida pelo aplicativo. O histórico da rota atual é armazenado na estrutura, mas não em um estado que a estrutura possa reconstruir (já que são todas as instâncias de objetos fornecidos pelo código do aplicativo). A posição de rolagem é praticamente a única coisa que podemos realmente armazenar (e salvamos isso em uma loja atualmente, mas não em uma que sobreviva ao aplicativo).

Mas, em qualquer caso, deveríamos definitivamente fazer melhor do que hoje.

Como um desenvolvedor Android experiente, gostaria de participar da conversa com os desenvolvedores iOS também quando for discutido, de modo que flutter pode ser a estrutura perfeita para construir aplicativos iOS e Android.
Acho que a capacidade de persistência do framework pode ser importante para salvar dados, preferências, dados de cache e estados da maneira mais amigável possível para o desenvolvedor.

Uau, eu não sabia que era esse o caso? Isso é realmente crítico, e vale a pena mencionar que em dispositivos com menos memória, o processo pode ser eliminado com bastante facilidade!

Que tal salvar PageStore (ou relacionado) como um fluxo de bytes por meio de serialização.dart em onSaveInstanceState ?

@Takhion Você poderia

@LouisCAD com certeza está aqui: https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/widgets/page_storage.dart

Mas acho que você precisará economizar mais do que isso: pelo menos a (s) rota (s) atual (is) e talvez algum estado?
O que você acha @Hixie?

No momento, não fazemos nada para tornar isso fácil. Ainda não estudamos esse problema em detalhes. Por enquanto, eu recomendo armazenar as informações que você deseja persistir manualmente e aplicá-las novamente quando o aplicativo for restaurado.

Expomos os eventos de ciclo de vida necessários ("você está prestes a ser morto!", "Parabéns, agora você está restaurado") no código Dart, para que os desenvolvedores possam lidar com o estado persistente? Essa parece ser a capacidade suficiente para desbloquear os desenvolvedores para explorar soluções. Pensamentos?

@sethladd Apenas expô-lo aos desenvolvedores não é IMHO suficiente. O Flutter também precisa salvar o estado da IU, sem que os desenvolvedores tenham que fazer isso manualmente repetidamente para cada aplicativo com código clichê verboso sujo e dificilmente sustentável.

@LouisCAD obrigado pelo feedback. Estou pensando em etapas ... qual é a etapa um? Ainda temos os eventos do ciclo de vida expostos?

@sethladd tudo o que consegui encontrar foi isso , que não expõe nenhum retorno de chamada para salvar / restaurar o estado da instância

@Hixie, você mencionou em # 3427 que temos ganchos para o ciclo de vida. Você quis dizer https://docs.flutter.io/flutter/widgets/WidgetsBindingObserver-class.html ?

O Flutter está usando várias atividades por padrão? (por exemplo, se você for para uma nova tela).

A morte do processo é bastante comum, o usuário clica no botão Home e inicia algum outro aplicativo de uso intensivo de memória / CPU e seu aplicativo será encerrado em segundo plano (ou seu aplicativo ficou em segundo plano por muito tempo). É uma parte integrante do sistema operacional Android - cada tela (atividade) deve ser capaz de persistir e restaurar seu estado e o processo pode ser encerrado a qualquer momento após onStop ().

O Android recriará o backstack de Activities se o usuário retornar a um aplicativo desativado, a Activity principal é criada primeiro e, em seguida, as atividades no backstack são recriadas sob demanda se você voltar no histórico de backstack. Este pode ser um problema maior caso o Flutter esteja usando várias atividades (não tenho certeza se usa).

Isso significa que, se você não tiver implementado o salvamento de estado, o sistema recriará as Atividades, mas todo o resto será perdido (o processo foi encerrado), levando a inconsistências e travamentos.

Escrevi um artigo sobre processo de morte no Android https://medium.com/inloop/android-process-kill-and-the-big-implications-for-your-app-1ecbed4921cb

Além disso, não há uma maneira (limpa) de evitar que o Android elimine seu aplicativo depois de passar do estado onSaveInstanceState (Bundle) . Portanto, talvez o Flutter deva ser capaz de encaminhar esse retorno de chamada onSaveInstanceState da Activity para suas "telas". Você obterá o Bundle com o estado salvo de volta no retorno de chamada do ciclo de vida onCreate (Bundle savedInstanceState) ou onRestoreInstanceState (Bundle savedInstanceState) em sua atividade.

Então, para recapitular - o Flutter poderia encaminhar os callbacks onSaveInstanceState () e onRestoreInstanceState () para o desenvolvedor. Idealmente, você também envolveria o objeto Android Bundle em algo que também pudesse ser usado no Flutter. A próxima etapa seria que todos os widgets Flutter dentro da tela também fossem notificados sobre esses retornos de chamada e os usassem para persistir em seu estado atual.
O sistema operacional Android pegará o pacote e o manterá no disco para que não seja perdido caso o processo seja encerrado e possa ser desserializado novamente.

Boa sorte com isso :-). Por favor, tome cuidado e não introduza muito desse estado / inferno do ciclo de vida do Android no Flutter.

Não tenho certeza de como isso funciona no iOS - mas acho que há algo semelhante, mas não é "necessário" usá-lo (?).

Um retorno de chamada do ciclo de vida seria bom para mim. Gostaria apenas de armazenar / carregar o estado redux serializado.

Obrigado por todos os comentários! @Takhion também se ofereceu para ajudar aqui. Talvez um design de API e uma biblioteca sejam um bom começo? Se essa biblioteca funcionar, podemos procurar uma integração mais formal. Além disso, a biblioteca ajudará a identificar o que precisamos fazer no mecanismo de baixo nível (se houver). Basicamente: qual é a proposta concreta da API?

O Flutter está usando várias atividades por padrão? (por exemplo, se você for para uma nova tela)

@DanielNovak Flutter usa seu próprio sistema de "roteamento" e por padrão se integra com Android através de uma única View em uma única Activity. Você pode ter um aplicativo Android / Flutter híbrido e, como tal, potencialmente, várias atividades e visualizações de flutuação, mas, nesse caso, você pode facilmente salvar / restaurar o estado da instância por meio do Android diretamente.

Eu apenas armazenaria / carregaria o estado redux serializado

@zoechi, você provavelmente não quer fazer isso porque o pacote de estado de instância salvo precisa passar pelo IPC com um limite rígido de 1 MB para todo o estado do aplicativo Android. O estado da instância, por definição, deve ser apenas os pedaços de dados que permitiriam a você recriar as mesmas condições de qualquer atividade em andamento que o usuário está realizando, então: entrada de texto, posição de rolagem, página atual, etc. Qualquer outra coisa deve ser persistido no disco ou derivado de outro estado.

Flutter expõe o evento de ciclo de vida onPause do evento de ciclo de vida onDestroy () do Android não é exposto. Acho que a abordagem certa seria conectar-se ao evento onPause () para armazenar o estado relacionado à instância.
Para ser compatível com onSaveInstanceState () e onRestoreInstanceState () do Android, talvez faça sentido adicionar métodos semelhantes aos widgets Flutter.
@sethladd @LouisCAD Alguém criou uma proposta de design?

@ raju-bitter onPause () não é ideal, é melhor conectar-se a onSaveInstanceState (). Aqui está o javadoc de onSaveInstanceState que menciona que onPause () é chamado mais regularmente do que onSaveInstanceState (você acionaria o salvamento de estado com mais frequência do que o necessário ou quando não fosse necessário):

Não confunda este método com callbacks do ciclo de vida da atividade, como onPause (), que é sempre chamado quando uma atividade está sendo colocada em segundo plano ou a caminho da destruição, ou onStop () que é chamado antes da destruição. Um exemplo de quando onPause () e onStop () são chamados e não este método é quando um usuário navega de volta da atividade B para a atividade A: não há necessidade de chamar onSaveInstanceState (Bundle) em B porque essa instância específica nunca será restaurada , então o sistema evita chamá-lo. Um exemplo quando onPause () é chamado e não onSaveInstanceState (Bundle) é quando a atividade B é iniciada na frente da atividade A: o sistema pode evitar chamar onSaveInstanceState (Bundle) na atividade A se não for eliminado durante a vida de B desde o estado da interface do usuário de A permanecerá intacto.

@Takhion

o pacote de estado da instância deve passar pelo IPC com um limite rígido de 1 MB para todo o estado do aplicativo Android

Esse não é o meu problema.
Eu só preciso saber quando persistir / restaurar, persistir no disco é bom para mim.

Propagar o onSaveInstanceState / onRestoreInstanceState é a parte fácil: a vibração está contida em uma visualização e tem acesso adequado a esses retornos de chamada. O que estou tentando fazer é criar uma proposta de design e um protótipo para serializar automaticamente os objetos contidos em um certo "repositório", para que usá-lo seja indolor (ao contrário de ter que fazer tudo manualmente).

@Takhion você é o vento sob minhas asas. Mal posso esperar para ver a proposta!

OK, estou construindo um PR para isso e encontrei um obstáculo: [ View.onSaveInstanceState ] (ou [ Activity.onSaveInstanceState ], para o caso) requerem fornecer o estado da instância salva de forma síncrona e de minha experiência (e este [comentário]) parece que as mensagens do host / plataforma podem

@sethladd @Hixie @eseidelGoogle @ jason-simmons diga que há uma maneira simples de fazer uma única chamada síncrona / bloqueadora do Android para o Flutter que não envolva código C / C ++ ad-hoc, uma vez que [ dart:jni foi aposentado]?

Cc @ mravn-google

A natureza assíncrona das mensagens da plataforma vem do fato de que a IU do Dart é executada em um thread diferente daquele usado para os callbacks da plataforma Android. Mas você sempre pode transformar uma chamada de API assíncrona em uma síncrona usando uma trava:

final MethodChannel channel = new MethodChannel(messenger, someName);
final CountDownLatch latch = new CountDownLatch(1);
channel.invokeMethod("someMethod", someArguments, new MethodChannel.Result() {
  <strong i="6">@Override</strong>
  public void success(Object result) {
    // handle successful invocation
    latch.countDown();
  }
  <strong i="7">@Override</strong>
  public void error(String errorCode, String errorMessage, Object errorDetails) {
    // handle failed invocation
    latch.countDown();
  }
  <strong i="8">@Override</strong>
  public void notImplemented() {
    // handle invocation of unimplemented method
    latch.countDown();
  }
});
try {
  latch.await();
} catch (InterruptedException e) {
  // handle interruption
}

Você pode declarar uma variável final AtomicReference<SomeType> (ou semelhante) ao lado da trava, se precisar transmitir valores das localizações // handle xxx para o código que é executado após os retornos de latch.await() .

obrigado @ mravn-google!

@ mravn-google, essa foi exatamente a primeira abordagem que tentei, mas a conversa principal no Android apenas trava na chamada await() para sempre :(

@Takhion Certo, é claro.

você sempre pode transformar uma chamada de API assíncrona em uma síncrona usando uma trava

... a menos que a resposta assíncrona deva ser entregue ao thread em que você já está: - /

Parece que precisamos, então, de algum tipo de handshake de sincronização para onSaveInstanceState . Sabemos de outros retornos de chamada semelhantes que exigem uma resposta de sincronização envolvendo cálculos do lado do Dart? Isso exigiria uma troca de mensagens de plataforma síncrona de propósito geral. Do contrário, poderíamos fazer algo especificamente para onSaveInstanceState .

@ mravn-google, infelizmente, o Android está _cheio_ dessas desagradáveis ​​"janelas de oportunidade síncronas", onde tudo precisa acontecer antes de retornar da chamada do método! No topo da minha cabeça: muitos Activity eventos de ciclo de vida, métodos em Broadcast Receiver , SyncAdapter , etc. Estou honestamente surpreso por não ter sido um problema maior antes!

Em minha opinião, se quisermos que o Flutter realmente alcance paridade de recursos com o que é possível por meio de APIs "nativas", então ter comunicação de mensagem síncrona é uma necessidade.

@ mit-mit eu quis dizer SystemChannels.lifecycle e qualquer coisa que use isso, sim.

@sethladd acho que sim.

@ mravn-google, obrigado! Fechei o # 7560 em favor deste, apenas para desduplicar e centralizar a conversa.

Estou inclinado a adicionar uma contraparte síncrona a MethodChannel para esse tipo de comunicação de plataforma para Dart.

@ mravn-google, como está indo? Posso ajudar?

Obrigado @Takhion ! Deve haver em breve. Estou trabalhando em um canal de método síncrono e espero ter um PR dentro de um ou dois dias. Assim que estiver lá, vou pedir sua revisão. Quero conectar o novo canal a onSaveInstanceState mais rápido possível para testar se tudo funciona como planejado nesse cenário. Se você já tem um aplicativo de exemplo com o qual poderia contribuir, seria incrível.

@ mravn-google incrível! Não tenho um aplicativo porque estou modificando a estrutura diretamente, mas certamente posso fornecer uma amostra. A propósito, estou usando BinaryMessages diretamente, pois não há necessidade de um codec no lado do Android (são apenas bytes passados), está certo?

Vocês são foda.

@Takhion O uso direto de BinaryMessages está bem, se você não precisa de um codec. Estarei adicionando um

void setSynchronousMessageHandler(String channel, ByteData handler(ByteData message))

para essa classe.

@Takhion Strike that. Não podemos simplesmente fazer chamadas síncronas na VM do Dart para invocar handler . Portanto, BinaryMessages permanecerá inalterado. Em vez disso, tudo será assíncrono do ponto de vista do código Dart, como de costume. A mudança será feita em BinaryMessenger e suas implementações no lado da plataforma. Vou acrescentar

ByteBuffer sendSynchronous(String channel, ByteBuffer message);

O código de visualização da plataforma que oferece suporte a FlutterView implementará isso enviando uma mensagem de plataforma assíncrona e aguardando uma trava. O objeto de resposta incluído com essa mensagem de plataforma não postará um encerramento de tratamento de resposta para o encadeamento da plataforma (que agora está bloqueado), mas, em vez disso, armazenará a resposta e liberará a trava.

@ mravn-google funciona para mim, pois não nos impede de enviar dados do lado do Dart, mesmo que seja uma chamada assíncrona: +1:
Eu acho que não haverá nenhuma otimização se alguém usar Future.value(...) na resposta assíncrona do DART.

@Takhion Por favor, dê uma olhada em https://github.com/flutter/engine/pull/4358

Vejo que você está falando principalmente sobre a plataforma Android aqui. Mas, na verdade, o iOS também tem um mecanismo semelhante para salvar / restaurar o estado da IU quando o aplicativo foi encerrado por algum motivo. Por exemplo, quando o usuário muda para um aplicativo diferente, o aplicativo atual é suspenso e pode até ser encerrado devido a restrições de memória. Você pode encontrar mais informações sobre este processo nos documentos oficiais.

Os desenvolvedores do iOS esperam que esse procedimento de preservação / restauração seja quase automático. Eu acho que, se seu objetivo é fazer um framework verdadeiramente multiplataforma, você definitivamente deve levar isso em consideração.

Você pensaria que algo que pretende direcionar o Android como uma plataforma, pelo menos, tenta obedecer aos princípios básicos do contrato de atividade ....

@Zhuinden não vamos reclamar de coisas que ainda estão em beta? :)

Outras api síncronas são onLowMemory e onTrimMemory e onConfigurationChanged não sei se IOS também tem APIs ou mecanismos semelhantes

Podemos não querer reproduzir o mecanismo de salvamento e restauração do Android defeituoso no Flutter: você não pode salvar todo o estado no pacote por causa do limite de tamanho da transação.

No Android, você acaba tendo várias lógicas de salvamento / restauração de estado:

  • salvouInstanceState do desenvolvedor, mas você não pode armazenar objetos grandes dentro, como uma lista de elementos
  • banco de dados, onde você pode armazenar objetos grandes
  • a maioria dos widgets também gerencia seu próprio estado
  • atividade / pilha de fragmentos

É realmente fácil para os desenvolvedores implementar lógicas confusas e com erros. Em minha própria experiência, é realmente raro ver o salvamento e a restauração de estado feitos da maneira certa.

Na vibração, o estado do desenvolvedor é a única fonte de verdade.
Os desenvolvedores devem manter esse estado no disco quando ele muda, sem passar pela mecânica limitada da plataforma nativa.

Claro, você não conseguirá salvar facilmente a posição de rolagem, mas quem se importa.
Na maioria das vezes, uma boa UX para restauração significa que o usuário deve ver a mesma tela que estava antes. Você já pode fazer isso com o Flutter hoje.

Resumindo: há espaço para melhorias no lado do Flutter, a fim de tornar o salvamento / restauração de estado menos clichê (para coisas como o Navigator). Mas não acho que ele deva fornecer um gancho para o estado de salvamento / restauração da plataforma.

Claro, você não conseguirá salvar facilmente a posição de rolagem, mas quem se importa.

Todos? Eu ficaria muito frustrado se o aplicativo redefinisse sua lista toda vez que eu acidentalmente acionar uma mudança de orientação. : p

@Saketme FWIW, Flutter não redefine as posições de rolagem na mudança de orientação.

Usar um aplicativo Flutter com um pouco de multitarefa deve ser divertido em um dispositivo Android Go! Progresso perdido toda vez que 1 GB de RAM ou menos está sob pressão

@Saketme : como disse @ mravn-google, o Flutter não está vinculado a alterações de configuração como as atividades do Android.
O único gerenciamento de estado necessário é quando o aplicativo é encerrado pelo sistema operacional enquanto está em segundo plano.

@LouisCAD O salvamento e restauração do estado
Os desenvolvedores do Flutter já podem implementar a maior parte dessa lógica sozinhos, e o Flutter provavelmente deve fornecer maneiras de torná-lo menos clichê. No entanto, o mecanismo oferecido pelo Android é falho e poderíamos aproveitar a oportunidade com o Flutter para projetar algo melhor, em vez de invadir a Activity onSaveInstanceState / onRestoreInstanceState .

Qual é a situação atual desse problema?

Considere o caso de um aplicativo de bate-papo, onde estou digitando uma longa mensagem no TextField .
De repente recebo um telefonema e depois de algum tempo, o SO mata o app de chat por falta de memória.
Após a ligação, volto para o aplicativo de bate-papo. O Flutter persistirá automaticamente e recriará a mensagem que eu estava digitando? ou O aplicativo precisa persistir e recriá-lo manualmente?

Além da posição de rolagem e da rota, o que mais são "estados" que geralmente não são gerenciados pelos desenvolvedores?

@nsreenath Qualquer aplicativo de bate-papo deve manter o rascunho de mensagens sozinho a cada poucos caracteres e ao ir para o plano de fundo. Você pode digitar uma mensagem longa e, se o dispositivo desligar por algum motivo, você não vai querer perder seu progresso nisso. O estado da instância do Android é para entrada do usuário leve e progresso nas telas

Na verdade, eu dormi sobre isso e cheguei à conclusão de que é muito fácil para os desenvolvedores do Flutter detectar se o aplicativo voltou da morte do processo ou é uma instância completamente nova.

Muito simples, desde que eles verifiquem savedInstanceState == null (assumindo que eles colocaram pelo menos 1 item em onSaveInstanceState(bundle) ). Se um sinalizador booleano estático isFirstInit == false e savedInstanceState != null , é após a morte do processo.

Em seguida, eles podem criar qualquer tipo de retorno de chamada de serialização / desserialização para o estado complexo, que pode ser apagado automaticamente quando detectam savedInstanceState == null . Dessa forma, eles só precisam persistir algum booleano aleatório, mas podem gerenciar seu próprio sistema de persistência.

Muito simples, contanto que eles verifiquem o savedInstanceState == null (assumindo que eles colocaram pelo menos 1 item no onSaveInstanceState (pacote)).

Não há savedInstanceState e onSaveInstanceState no iOS. Mas o aplicativo ainda pode ser morto pelo sistema operacional.

Considere um fluxo de trabalho típico:

  • Um usuário vai para uma tela específica dentro do aplicativo
  • Em seguida, o usuário muda para outro aplicativo
  • De repente, o sistema operacional inteligente decide encerrar o aplicativo anterior para preservar a memória
  • O usuário retorna ao aplicativo anterior, mas vê a tela inicial. Não a tela que estava aberta quando ela decidiu trocar o aplicativo

Então, qual é atualmente a maneira recomendada de preservar o histórico da tela no Flutter?

PS Claro, isso não é importante se seu aplicativo tiver apenas uma tela: sorriso:

@Zhuinden Não consigo ver muitos cenários em que isso seja necessário de qualquer maneira.

a) Exemplo de bate-papo:
Sempre salve e restaure o conteúdo anterior escrito no campo de texto que não foi enviado.

b) Exemplo de formato longo:

  1. Sempre salve o conteúdo
  2. Quando o usuário retornar à tela do formulário, notifique-o de que há uma versão de rascunho que foi salva, mas não enviada, perguntando se ele deseja restaurá-la. Não faz nenhuma diferença se o usuário saiu do formulário manualmente (talvez pergunte se o usuário deseja salvar o rascunho) ou se o aplicativo foi encerrado enquanto estava em segundo plano.

Provavelmente, existem alguns casos em que o conhecimento específico de "nova tela" versus "nova tela após a morte do processo" é útil, mas isso pode não ser tão comum quando você projeta o conteúdo do seu aplicativo para ser sempre apoiado por um banco de dados.

@storix Você precisa persistir manualmente a rota atual e restaurá-la quando o aplicativo for iniciado novamente. Muito clichê para um caso de uso comum, eu concordo. Mas ainda é viável.
Além disso: Muitos desenvolvedores de Android ficariam surpresos com a quantidade de aplicativos / desenvolvedores de iOS que se importavam com a restauração de estado. A maioria dos desenvolvedores que conheço nem mesmo está ciente de que isso é um problema. Acho que é o luxo de trabalhar apenas em dispositivos de ponta.

@Zhuinden os retornos de chamada do ciclo de vida de https://github.com/flutter/flutter/issues/6827#issuecomment -335160555 funcionaram para meu aplicativo de bate-papo. Acabei de salvar algum estado sempre que o aplicativo é colocado em segundo plano. Veja também https://docs.flutter.io/flutter/dart-ui/AppLifecycleState-class.html

Gostaria de exortar a equipe do Flutter a não expor os métodos de ciclo de vida do Android / iOS como a maneira canônica de persistir / restaurar o estado da IU. Mesmo em aplicativos nativos, eles eram difíceis de entender e codificar corretamente, e a linha entre a IU e o estado do aplicativo costumava ser confusa, com diferentes mecanismos fornecidos para cada um. Seria uma pena promulgar todas essas APIs díspares no Flutter (especialmente se plataformas adicionais forem direcionadas no futuro).

Os widgets sem estado do Flutter _already_ migraram a responsabilidade pelo estado da IU fora dos widgets e para dentro do aplicativo, então o problema de persistir / restaurar o estado da IU é cada vez mais coberto pela persistência / restauração do estado _app_. Vejo isso como uma coisa boa, em vez de tratar a interface do usuário e o estado do aplicativo de maneira diferente, como no iOS e no Android.

Percebo que alguns widgets Flutter, como ScrollView e TextField, fornecem classes "Controller" que encapsulam o estado dinâmico do widget. De certa forma, eles estão agindo como objetos "memento" - um instantâneo do estado do widget - que pode ser fornecido ao widget posteriormente para restaurar seu estado para alguma configuração anterior. Soa familiar. Se essas "lembranças" fossem fáceis de persistir / restaurar, um aplicativo poderia assumir a responsabilidade por elas como parte do gerenciamento de seu próprio estado. Isso encerraria a dicotomia do estado do aplicativo / IU, seria opt-in e evitaria a necessidade da estrutura Flutter de fornecer ganchos de preservação do estado da IU (menos é mais!)

Em outras palavras, faça com que seja responsabilidade do desenvolvedor, mas tente torná-lo o mais fácil possível. Se um desenvolvedor acha que um formulário preenchido pela metade é importante o suficiente para que o Flutter persista nas invocações, então pode-se argumentar que provavelmente é importante o suficiente para ser considerado o estado do aplicativo. Sei que persistir o estado dos objetos apresenta problemas de controle de versão, mas acho que esse é um problema mais gerenciável.

Houve algum progresso neste assunto? Acho que há uma necessidade real de distinguir entre o estado do processo de 'primeira execução' e 'restaurado'. De que outra forma você pode programar para mostrar a tela certa quando o usuário navegar de volta para um processo eliminado?

@lukaspili, você escreveu: "O único gerenciamento de estado necessário é quando o aplicativo é

Ok, então o que acontece com a pilha do roteador, também salvar em disco? Também não t find proper solution to determine that application was restored. Every time when app goes to background I need to save router stack, app data, state to disc? What if I don t uso Redux?

Acho que, como primeiro passo, devemos dar aos plug-ins a possibilidade de salvar e restaurar o estado. Aqui está minha proposta: # 22328

Houve uma atualização sobre este problema?

Eu estou enfrentando o mesmo problema. Eu tenho uma solução alternativa para salvar entradas de texto aqui por meio do banco de dados em tempo real do Firebase. Mas o método é bastante rudimentar e geralmente não tem a opção de escalabilidade, pois cada usuário será redirecionado para o mesmo banco de dados. Portanto, meus dados estão disponíveis para todas as outras pessoas com acesso a uma conta do gmail. Tentei usar o Redis, mas embora possa criar listas separadas no mesmo banco de dados para outros usuários, isso sempre é feito por meio de um método POST. Isso significa que a visualização deve ser atualizada sempre que um novo dado é armazenado. O que significa nenhum visual em tempo real. Uma grande falha, de fato.

Há um problema maior aqui. Tive algumas idas e vindas com @matthew-carroll há dois meses e estou bastante confiante de que isso está acontecendo.

Comecei a fazer uma biblioteca que lida com isso . Na verdade, eu não recomendaria usá-lo, mas é uma ideia de uma implementação apenas do Dart. # 6225 tornou as coisas um pouco complicadas.

Percebi que o aplicativo do Google Ads, supostamente feito com Flutter, preserva o estado de navegação após o reinício de uma atividade. Seria uma boa referência saber como isso é implementado, mesmo que seja uma solução alternativa.

@aletorrado O aplicativo do Google Ads preserva o estado de navegação caso o usuário tenha descartado o aplicativo do “gerenciador de tarefas” do Android / IOS e posteriormente reiniciado?

@gitspeak Não, ele apenas preserva o estado se a atividade foi eliminada automaticamente pelo sistema operacional (talvez usando o gancho onSaveInstanceState).

@aletorrado Então, é provavelmente a única maneira ...

Na verdade, tentei entrar no aplicativo do Google Ads, mas você precisa ter uma conta ativa para acessar qualquer tela além da primeira: DI não consigo testar se realmente funciona ou não

É verdade. Garantido. Ele também preserva outro estado, como a posição de rolagem.

Para esclarecer alguma confusão:

  • Sim, o aplicativo do Google Ads foi desenvolvido com Flutter.
  • Sendo um dos primeiros a adotar, eles tiveram que fazer algo incomum para a execução em segundo plano. Eles criam e mantêm seu principal isolado quando o aplicativo é iniciado (não a atividade). Em seguida, eles passam esse isolado para a visualização Flutter quando ele é criado.
  • O que você está observando como "manutenção do estado" é um efeito colateral da metodologia descrita acima. Quando você mata a atividade no Google Ads, a "IU" do Flutter não morre. Ele é mantido na memória vinculado ao contexto do aplicativo. Quando a atividade é reiniciada, carrega o mesmo isolado e viola, você recebe sua IU de volta.

Não recomendamos essa abordagem para resolver esse problema porque ela não resolve o problema de forma alguma. Se o processo de aplicação morre, todo o estado ainda está perdido. Você pode tentar isso com o Google Ads indo e interrompendo o processo em Configurações> Aplicativos. A IU será perdida.

Pode haver uma maneira de salvar o estado isolado localmente, mas é necessário mais investigação porque não temos certeza de quão viável / seguro é.

Por enquanto, as soluções descritas acima são as melhores: Conecte-se a métodos nativos e salve seletivamente o estado do seu aplicativo para que possa restaurá-lo mais tarde. Uma solução automática com suporte para framework em iOS e Android ainda está longe.

Você pode tentar isso com o Google Ads indo e interrompendo o processo em Configurações> Aplicativos. A IU será perdida.

não, isso está totalmente ok. Forçar a parada de um aplicativo deve perder o estado.

Ser encerrado por condição de pouca memória não deveria.

De qualquer forma, se eles mantiverem o estado no aplicativo como um singleton na memória sem persistir nada no disco / pacote, ele SERÁ perdido.

É uma pena não poder testar o aplicativo sem uma conta, pois não vou criar uma conta do Google Ads apenas para mexer no próprio aplicativo. : thinking_face:

Conecte-se a métodos nativos e salve seletivamente o estado de seu aplicativo para que possa restaurá-lo mais tarde.

Na verdade, a melhor solução seria persistir o estado ativo no disco e detectar a morte do processo no lado nativo do Android; se você detectar a morte do processo, recarregue do disco, caso contrário, limpe-o e reinicie do zero.

Não sei o suficiente sobre os navegadores do Flutter para dizer se você pode reconstruir o histórico de navegação com algum tipo de método setHistory ou setBackstack como você faria com um backstack ou Condutor ou algo parecido.

Atividades / Fragmentos lidam com essas coisas internamente com ActivityRecord / FragmentRecord, então eles não são mencionados além disso.

Obrigado por todos esses insights @mehmetf. É triste saber que o suporte para salvar / restaurar o estado está longe de ser implementado.

Talvez flutter_redux possa ajudar a serializar parte do estado do aplicativo.

Bem, se você persistir o blob da loja Redux no disco. Apenas tome cuidado com o problema da "caixa de diálogo de carregamento infinito após a morte do processo". : thinking_face:

Para esclarecer alguma confusão:

Na verdade, estou um pouco mais confuso ..

Sendo um dos primeiros a adotar, eles tiveram que fazer algo incomum para a execução em segundo plano. Eles criam e mantêm seu principal isolado quando o aplicativo é iniciado (não a atividade). Em seguida, eles passam esse isolado para a visualização Flutter quando ele é criado.

Como isso se relaciona com o problema em questão? Os isolados Dart são executados em um processo separado?

O que você está observando como "manutenção do estado" é um efeito colateral da metodologia descrita acima. Quando você mata a atividade no Google Ads, a "IU" do Flutter não morre. Ele é mantido na memória vinculado ao contexto do aplicativo. Quando a atividade é reiniciada, carrega o mesmo isolado e viola, você recebe sua IU de volta.

Meu entendimento é que @aletorrado observou “manutenção de estado” depois que o processo foi encerrado, possivelmente quando o aplicativo foi movido para o segundo plano (ag pressionando o botão home).

Para sua informação: quando uma “atividade morre” no tempo de execução, é SEMPRE seguido pelo encerramento do processo.

Não recomendamos essa abordagem para resolver esse problema porque ela não resolve o problema de forma alguma. Se o processo de aplicação morre, todo o estado ainda está perdido. Você pode tentar isso com o Google Ads indo e interrompendo o processo em Configurações> Aplicativos. A IU será perdida.

Como @Zhuinden mencionou, este não é um cenário válido de "manutenção de estado", portanto, descartar o aplicativo do "gerenciador de tarefas"

Pode haver uma maneira de salvar o estado isolado localmente, mas é necessário mais investigação porque não temos certeza de quão viável / seguro é.

Essa não é a maneira de fazer isso. Para Android, você deve se conectar ao mecanismo onSaveInstanceState, pois foi feito explicitamente para salvar o estado da Atividade para aqueles cenários em que a "manutenção do estado" é necessária após o término do processo. Não estou familiarizado com o ciclo de vida de aplicativos IOS.

Por enquanto, as soluções descritas acima são as melhores: Conecte-se a métodos nativos e salve seletivamente o estado do seu aplicativo para que possa restaurá-lo mais tarde. Uma solução automática com suporte para framework em iOS e Android ainda está longe.

Nenhuma solução foi descrita em vez de reconhecer o recurso Android apropriado para reter o estado para os casos em que o tempo de execução decide encerrar o processo.

Nenhuma solução foi descrita em vez de reconhecer o recurso Android apropriado para reter o estado para os casos em que o tempo de execução decide encerrar o processo.

Eu meio que descrevi o que podemos fazer, mas não sei o suficiente sobre o Flutter para dizer se ele suporta o que é necessário para ele (a capacidade de configurar um histórico de navegação arbitrário a qualquer momento 😄)

@Zhuinden, há um "zilhão" de casos extremos cobertos por onSaveInstanceState em que se refere a Atividades / Controles ... acredite em mim ... mas você é bem-vindo para investigar como um exercício educacional.

... e apenas para reiterar minha posição: Android Tasks / Activities, Services, Application Life-cycle (incluindo onSaveInstanceState) são todos parte do Android Programing Contract "incidentalmente" eles são realizados primeiro em Java, mas a principal conclusão é que eles são meios de impor restrições de nível de sistema no processo do

Caros desenvolvedores Android,

obrigado por sua paixão em fornecer a melhor experiência de usuário aos seus usuários, salvando o estado não apenas nas alterações de configuração, mas também durante a morte do processo. Você deve ter lido nos 200 comentários acima que o Flutter está tendo dificuldades para salvar o estado do aplicativo. Eu gostaria de dizer por que e como resolver isso .

Por que não posso usar onSavedInstanceState ?

onSavedInstanceState requer que os desenvolvedores salvem o estado do aplicativo de forma síncrona. Você tem que voltar imediatamente. Mas a comunicação com o Flutter é assíncrona por padrão ( Future ). Portanto, você não pode solicitar o estado salvo do seu aplicativo Flutter quando o Android solicitar.
_Mas você poderia retornar ao estado salvo se o tivesse criado antes, não é? _

O problema não é que onSavedInstanceState esteja inacessível, o problema é o tempo. Quando o Android pede para salvar o estado, você não precisa de dados.

Solução de recuperação A: seu próprio arquivo de estado de instância salvo

Você pode ouvir onSavedInstanceState e informar seu aplicativo de flutter sobre este evento. Em seguida, salve o estado do aplicativo e escreva-o em um file . Quando seu aplicativo for recuperado da morte do processo (você pode detectar isso por meio de savedInstanceState no lado do Android), carregue o arquivo do sistema de arquivos, analise-o e recupere seu estado. (Dica: use canais de plataforma, eles são mais fáceis do que você imagina).

Você pode até mesmo recuperar o estado quando os usuários mataram o aplicativo ou descartar o estado salvo se for muito antigo!

Solução de recuperação B: salvar continuamente o estado

Poderíamos executar um cronômetro e salvar continuamente o estado do aplicativo no lado da plataforma na memória. Digamos a cada 3 segundos. Quando o Android chamar onSavedInstanceState , podemos retornar ao estado salvo anteriormente. (Sim, podemos perder as mudanças ocorridas nos últimos 3 segundos)

Solução de recuperação C: salvar o estado em pontos-chave

Como na solução B, o estado salvo será mantido na memória do lado da plataforma. Mas, em vez de salvar o estado a cada poucos segundos, você solicita manualmente o salvamento do estado depois que os usuários executam algumas ações. (abriu uma gaveta, digitou o texto em um TextField , você escolhe).


Todas as 3 soluções são combináveis, use o que melhor se adapta ao seu aplicativo.

Mas como posso salvar o estado do meu aplicativo?

Ao contrário do Android View s, Widget s não tem um retorno de chamada onSaveInstanceState . A árvore do widget não é salva automaticamente. E isso é bom! IU e estado são finalmente separados e não há mágica de reflexão ao restaurar o estado de exibição ou instanciar Activities and Fragments.

Para salvar o estado, você deve iterar em cada tela do aplicativo e salvar todas as informações necessárias para restaurá-los. Eu gosto de fazer isso em json, mas você pode escolher protobuf para melhor desempenho (meça antes de otimizar!).

Olhando para o nosso aplicativo de contador de amostra amado, isso pode ser super fácil:

// complete app state of sample application
{
  "counter": 24,
}

Se você tiver várias telas ou caixas de diálogo, poderá acabar com o seguinte app state json:

{
    "currentRoute": "/todo-detail/241",
    "routes": {
        "todo-detail/241": {
            "edited": "true",
            "title": "Buy bananas",
            "picture": "http://....",
            "show_confirm_delete_dialog": "false"
        },
        "todo-list": {
            "current_scroll_position": "365",
            "filter": "Filter.TODAY"
        }
    }
}

A implementação real depende muito da arquitetura do seu aplicativo.

  • Se você estiver usando o Redux, poderá usar um middleware para persistir o estado global do aplicativo em tempo real.
  • Se você estiver usando scoped_model , pode ser o suficiente para salvar os modelos. Recriá-los é fundamentalmente diferente de recriar um estado Redux.

As melhores dicas que posso dar:

  • Mantenha uma separação estrita de sua IU e seus modelos.
  • Olhe para cada tela e pergunte-se o que você realmente precisa para recuperar o estado. Na maioria das vezes, não é muito.
  • Fale com seu iOS colleagues e eles dirão que não implementaram a restauração de estado por meio de encodeRestorableState anos. Android 1M + vs iOS 974

Pascal

Ative a configuração "Não manter atividades".

Não, isso não é um teste adequado contra a morte do processo. Isso não acontece NUNCA, a menos que você habilite explicitamente essa configuração. 😞

Se você tiver várias telas ou caixas de diálogo, poderá acabar com o seguinte app state json:

{
  "currentRoute": "/todo-detail/241",
  "routes": {
      "todo-detail/241": {
          "edited": "true",
          "title": "Buy bananas",
          "picture": "http://....",
          "show_confirm_delete_dialog": "false"
      },
      "todo-list": {
          "current_scroll_position": "365",
          "filter": "Filter.TODAY"
      }
  }
 }

Nossa, estamos atingindo o "estado de navegação faz parte do estado do aplicativo como o estado de uma máquina de estados finitos" para que isso funcione, mas gosto da aparência 😄

Nossa, estamos atingindo o "estado de navegação faz parte do estado do aplicativo como o estado de uma máquina de estados finitos" para que isso funcione

😂 não me faça começar.

O restante do estado do aplicativo deve ser salvo em um banco de dados / no servidor. O único estado transitório que vale a pena salvar em onSaveInstanceState é a pilha de navegação e algumas entradas do usuário. Isso também é o que a maioria das pessoas está pedindo neste tópico.

O restante do estado do aplicativo deve ser salvo em um banco de dados / no servidor. O único estado transitório que vale a pena salvar em onSaveInstanceState é a pilha de navegação e algumas entradas do usuário. Isso também é o que a maioria das pessoas está pedindo neste tópico.

e essa é a CHAVE! (bem como suporte “Android Service”)

@gitspeak , a última troca que tivemos não teve nada a ver com esse problema. Eu vou esconder ambos. Se você deseja continuar a discussão, por favor me PM usando meu github handle @ gmail.com. Há claramente um mal-entendido aqui que está além do escopo deste problema.

Como um dos desenvolvedores que trabalha na interface entre o Flutter e o Android, gostaria de agradecer a @passsy por fornecer uma excelente visão geral dos problemas envolvidos com o salvamento do estado.

Também gostaria de acrescentar que estou interessado em entender quaisquer casos de uso específicos com os quais os desenvolvedores deste segmento estão lidando. O estado de salvamento é um conceito que provavelmente abrange muitos objetivos diferentes. O que um aplicativo chama de estado, outro não. É absolutamente necessário que um aplicativo tenha o estado salvo garantido, enquanto outro simplesmente prefere salvar algum estado, mas não será interrompido sem ele. Alguns aplicativos são totalmente desenvolvidos com Flutter, outros aplicativos devem sincronizar informações entre a IU tradicional do Android / iOS e o Flutter.

Se algum de vocês quiser descrever o caso de uso específico que seu aplicativo precisa suportar em relação ao estado de salvamento, sinta-se à vontade para me enviar um e-mail para [email protected] - Estou montando ativamente os casos de uso.

@passsy Por favor, considere meus comentários abaixo como uma tentativa sincera de fornecer feedback construtivo

Solução de recuperação A: seu próprio arquivo de estado de instância salvo
Você pode ouvir onSavedInstanceState e informar seu aplicativo de flutter sobre esse evento. Em seguida, salve o estado do aplicativo e grave-o em um arquivo. Quando seu aplicativo for recuperado da morte do processo (você pode detectar isso por meio de savedInstanceState no lado do Android), carregue o arquivo do sistema de arquivos, analise-o e recupere seu estado. (Dica: use canais de plataforma, eles são mais fáceis do que você imagina).
Você pode até mesmo recuperar o estado quando os usuários mataram o aplicativo ou descartar o estado salvo se for muito antigo!

Vários problemas:

1) Há uma condição de corrida inerente a essa abordagem. O encadeamento de "persistência de estado" pode muito bem sobreviver ao encadeamento principal do aplicativo Android, que invoca os eventos de ciclo de vida; nesse caso, o processo pode ser encerrado enquanto o arquivo está sendo escrito. Você pode tentar coordenar os dois encadeamentos bloqueando em um método de ciclo de vida em um sinal de conclusão do encadeamento de "economia de estado", mas que, além de introduzir complexidade de sincronização adicional, não pode garantir a exatidão, pois um atraso longo o suficiente pode introduzir ANR. (https://developer.android.com/topic/performance/vitals/anr)
Você pode argumentar que o "salvamento de estado" no arquivo é tão rápido que dificilmente aconteceria, mas considere os atrasos reais no agendamento do thread e na latência de armazenamento enquanto outros processos estão em execução, como downloads em segundo plano, atualizações da loja de jogos, reprodutor de mídia, etc ... Estou Não quero debater isso, mas minha experiência pessoal (compartilhada por outros) é que aparentemente os dispositivos Android não são imunes à mesma "gripe" que infecta PCs com Windows - eles também ficam mais lentos com o tempo. Eu limpei recentemente meu Nexus 7 e não só levou HORAS para baixar e instalar todos os aplicativos por Wifi, mas a capacidade de resposta do dispositivo agora é uma piada completa para apontar onde eu duvido ter paciência para usá-lo, mesmo para procurar receitas.

2) Você não pode ter uma definição confiável de "muito velho". Se eu descartar o aplicativo do gerenciador de tarefas, espero que todo o estado desapareça imediatamente, enquanto se eu mudar para outro aplicativo, espero que o estado seja preservado indefinidamente enquanto o telefone estiver ligado. A propósito, o Android não fornece uma maneira de distinguir entre esses dois estados.

3) Nem todos os controles de IU expõem seu estado por meio de uma API que permite ao usuário configurar o controle para um determinado estado. Essa API, sem dúvida, incorreria em esforços de desenvolvimento adicionais em nome do desenvolvedor do controle. No Android, é esperado que os controles de interface do usuário bem comportados abstraiam os problemas de "preservação de estado" do desenvolvedor, lidando com notificações onSave / Restore InstanceState internamente, controlando, assim, qual estado, se algum, for preservado. Se bem me lembro, o controle AutoComplete retém o termo da consulta, mas não a lista de resultados.

Solução de recuperação B: salvar continuamente o estado
Poderíamos executar um cronômetro e salvar continuamente o estado do aplicativo no lado da plataforma na memória. Digamos a cada 3 segundos. Quando o Android chama onSavedInstanceState, podemos retornar ao estado salvo anteriormente. (Sim, podemos perder as mudanças ocorridas nos últimos 3 segundos)

Discordo muito, além de ter os mesmos problemas em torno da "Solução A", adiciona um novo risco de abrir uma lata de worms de duplicação de dados.

Solução de recuperação C: salvar o estado em pontos-chave
Como na solução B, o estado salvo será mantido na memória do lado da plataforma. Mas, em vez de salvar o estado a cada poucos segundos, você solicita manualmente o salvamento do estado depois que os usuários executam algumas ações. (abriu uma gaveta, inseriu texto em um TextField, você escolhe).

Tendo os mesmos problemas em torno da "Solução A".

A árvore do widget não é salva automaticamente. E isso é bom! IU e estado são finalmente separados e não há mágica de reflexão ao restaurar o estado de exibição ou instanciar Activities and Fragments.

Discordo. A retenção automática da árvore de widgets do Android nos casos em que seu estado está sujeito a ser descartado é muito útil! Por que diabos você iria querer transferir essa responsabilidade em tudo para o desenvolvedor do aplicativo? Por que isso está em contraste com a separação do estado da interface do usuário? Na verdade, esta maneira automática de salvar / restaurar a Árvore de Widget parece um exemplo perfeito de separação de IU / Estado.

A "Solução A" parece perfeitamente plausível para mim ao usar bloqueios apropriados, e nenhum ANR deve ser acionado se a quantidade de trabalho for razoável. Além disso, ao adotar o mecanismo de notificação da plataforma, ele evita o trabalho de serialização desnecessário o tempo todo, o que pode ser caro para cada pressionamento de tecla.

Sobre a possibilidade de automatizar este comportamento, parece-me claro que não tendo mecanismo de reflexão no runtime do Dart, e não tendo uma forma estruturada de armazenar o estado na memória no Flutter, isso não é possível de forma alguma. No momento, a abordagem mais simples que posso pensar em fazer isso seria optar por manter o estado em um único objeto dinâmico (assim como this.props e this.state no React).

@aletorrado

A "Solução A" parece perfeitamente plausível para mim ao usar bloqueios apropriados, e nenhum ANR deve ser acionado se a quantidade de trabalho for razoável.

Eu concordo. quando ... Onde o modelo Android é muito mais simples e robusto, uma vez que não exige que o desenvolvedor lide com bloqueios, ele não persiste o estado transitório para o disco e retém o estado apenas para aqueles casos em que é relevante algo para você (flutter) não pode ficar sem se conectar aos eventos de ciclo de vida do Android e ao recurso onSaveInstance - para começar, então, para que serve?

Além disso, ao adotar o mecanismo de notificação da plataforma, ele evita o trabalho de serialização desnecessário o tempo todo, o que pode ser caro para cada pressionamento de tecla.

Não há nenhum requisito para persistir cada toque de tecla, mas sim um instantâneo consistindo de estados selecionados

Os dados devem ser armazenados no Bundle como um ByteArray para evitar
disparando qualquer ANR ou lag causado por I / O no encadeamento principal.

Na quarta-feira, 19 de dezembro de 2018, às 18h08 Alejandro Torrado [email protected]
escreveu:

A "Solução A" parece perfeitamente plausível para mim ao usar o apropriado
bloqueios e nenhum ANR deve ser acionado se a quantidade de trabalho for razoável.
Além disso, ao abraçar o mecanismo de notificação da plataforma, evita fazer
serialização desnecessária funciona o tempo todo, o que pode ser caro de fazer
para cada pressionamento de tecla.

Sobre a possibilidade de automatizar esse comportamento, parece-me claro que
não tendo mecanismo de reflexão no tempo de execução do Dart, e não tendo
forma estruturada de armazenar o estado na memória no Flutter, isso não é possível
por qualquer meio. No momento, a abordagem mais simples que posso pensar em fazer
isso seria optar por manter o estado em um único objeto dinâmico (assim como
this.props e this.state no React).

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/flutter/flutter/issues/6827#issuecomment-448671264 ,
ou silenciar o tópico
https://github.com/notifications/unsubscribe-auth/AGpvBUQk17YOsjLMHTWcdJHL9rR71u6lks5u6nKGgaJpZM4KwSuX
.

O verdadeiro truque é construir este ByteArray de que você fala: ligeiramente_smiling_face:

Eu prefiro dizer que o verdadeiro truque é ser capaz de restaurar este
ByteArray corretamente.

Na quarta-feira, 19 de dezembro de 2018, 18:41, Gabor Varadi [email protected] escreveu:

O verdadeiro truque é construir este ByteArray de que você fala 🙂

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/flutter/flutter/issues/6827#issuecomment-448682229 ,
ou silenciar o tópico
https://github.com/notifications/unsubscribe-auth/AGpvBep8AW2xkKECt6uyTjqRGwri9Pmoks5u6npKgaJpZM4KwSuX
.

Não. Depois de ter a matriz de bytes, é muito fácil. Apenas mostre um indicador de carregamento enquanto o carrega (supondo que você já disse a si mesmo com os canais que precisa carregá-lo).

O verdadeiro problema é a integração do Flutter, o que chamo de parte de restauração.
A navegação deve ser salva automaticamente, e a entrada do usuário também, assim como em
aplicativos Android nativos.

Por causa desse problema, os aplicativos Flutter não podem se integrar corretamente no Android, pois
eles não se comportam corretamente quando há pressão de RAM e o usuário muda
aplicativos nestas condições antes de voltar.

Na quinta-feira, 20 de dezembro de 2018, 2h42, Gabor Varadi [email protected] escreveu:

Não. Depois de ter a matriz de bytes, é muito fácil. Basta mostrar um carregamento
indicador enquanto você está carregando (supondo que você disse a si mesmo com
canais que você precisa para carregá-lo).

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/flutter/flutter/issues/6827#issuecomment-448827537 ,
ou silenciar o tópico
https://github.com/notifications/unsubscribe-auth/AGpvBR4rmSxelodAjZvcQ6kOwPd7yxKPks5u6ushgaJpZM4KwSuX
.

Muitos de nós discutiram isso um pouco mais aqui: https://github.com/mehmetf/flutter/issues/1. Pontos relevantes para este problema:

  • Para corrigir (https://github.com/flutter/flutter/issues/6827#issuecomment-447488177): o Google Ads foi testado com "Don't Keep Activities" ativado. O processo do aplicativo não morre nesse caso, então minha explicação em (https://github.com/flutter/flutter/issues/6827#issuecomment-447955562) permanece. Como @gitspeak e @Zhuinden apontaram, esta não é uma boa maneira de testar os cenários contra os quais estamos nos protegendo, pois não funciona assim na prática.

  • Sempre que o aplicativo está em segundo plano, View.onSaveInstanceState é chamado no thread de interface do usuário para cada visualização na hierarquia que tenha o salvamento habilitado. Portanto, uma boa solução do ponto de vista do desenvolvedor seria FlutterView implementar isso. Para evitar condições de corrida com o thread de interface do usuário (que você não pode / não deve bloquear), este método não deve alcançar o aplicativo Flutter, pois não há uma maneira síncrona de fazer isso.

  • O que quer que onSaveInstanceState salve não pode ser maior que 1 MB, portanto, salvar todo o estado isolado está fora de questão. Em vez disso, podemos encontrar uma maneira de dividir o código, os ativos e os dados contidos na memória isolada e despejar apenas a parte dos dados serializados em um parcelável.

  • Quando a visualização é restaurada posteriormente, FlutterView cria um isolado e descompacta a parte de dados salva anteriormente, se houver.

  • Quando o aplicativo está em segundo plano, o estado provavelmente deve parar de mudar no lado Flutter. Sempre haverá casos extremos, portanto, apresentar uma lista de práticas recomendadas para aplicativos Flutter seria bom acompanhar esta solução (considerando o ciclo de vida e a pressão da memória ).

  • Eu imagino que essa seja uma questão muito mais urgente quando se trata de mercados emergentes como a Índia, onde o Flutter deve rodar em telefones de baixo custo. Há rumores de que o Android Go também possui uma estratégia de gerenciamento de memória muito mais agressiva.

  • Eles criam e mantêm seu principal isolado quando o _aplicação_ começa (não a atividade). Em seguida, eles passam esse isolado para a visualização Flutter quando ele é criado.

você pode fornecer um exemplo deste código?

Eles criam e mantêm seu principal isolado quando o aplicativo é iniciado (não a atividade). Em seguida, eles passam esse isolado para a visualização Flutter quando ele é criado.

você pode fornecer um exemplo deste código?

Não posso fornecer um exemplo do tipo copiar / colar, mas aqui está o que está acontecendo:

Aplicativo

  • Em seu Application.onCreate , chame funções de inicialização para FlutterMain . ( startInitialization e ensureInitializationComplete ). Você pode encontrar referências a eles no repositório de flutter / motor.
  • Crie um novo objeto FlutterNativeView, passe na instância Application como contexto.
  • Execute seu pacote nesta visualização nativa (Procure exemplos de runFromBundle ).
  • Implemente uma interface neste aplicativo que permita retornar esta visão nativa (algo como FlutterNativeViewProvider ).

Atividade

  • Estenda FlutterFragmentActivity e substitua estes dois métodos:
  <strong i="26">@Override</strong>
  public FlutterNativeView createFlutterNativeView() {
    FlutterNativeViewProvider provider = (FlutterNativeViewProvider) getApplication();
    return provider.getFlutterNativeView();
  }

  <strong i="27">@Override</strong>
  public boolean retainFlutterNativeView() {
    return true;
  }

Algo nesse sentido deve ajudá-lo. Observe que seu aplicativo Flutter começará a ser executado antes que uma Activity (e, portanto, uma janela) esteja disponível. Portanto, você não deve chamar runApp() em sua função principal até receber um sinal de Activity informando que ela está pronta. Você pode fazer isso por meio de PlatformChannels e um local para sinalizar que seria em createFlutterNativeView ().

Exoneração de responsabilidade

  1. Como mencionei acima, não toleramos esse padrão, pois ele realmente não resolve esse problema específico e é bastante confuso.

  2. Observe que muitas dessas APIs estão sujeitas a mudanças. Como os casos de uso de add2app ainda estão evoluindo, as APIs de incorporação do Android e iOS ainda não estão bastante estáveis. Anunciaremos as mudanças mais recentes no flutter-dev @ como de costume.

@passsy , @gitspeak

Solução de recuperação A: seu próprio arquivo de estado de instância salvo

Para ter certeza de que possui um arquivo de estado válido, você pode gravar em um arquivo .tmp e renomear quando a gravação for concluída. Se você também mantiver o arquivo de estado anterior, sempre terminará com o estado válido. Além disso, quando o processo é eliminado ao salvar o estado.

@jsroest

você sempre terminará com um estado válido.

Mas não necessariamente o estado correto, que é quase todo o ponto ..

Também não há nenhum requisito para o estado persistente em memória não volátil para começar.

@gitspeak

você sempre terminará com um estado válido.

Mas não necessariamente o estado correto, que é quase todo o ponto ..

Também não há nenhum requisito para o estado persistente em memória não volátil para começar.

Em muitas situações, ter um estado consistente é mais importante do que ter 'a maioria dos valores mais recentes'. Bancos de dados relacionais usados ​​com transações são um exemplo disso. Que na minha opinião também é um ótimo lugar para armazenar estado.

Persistir na memória não volátil tem algumas vantagens; O processo também é eliminado em 'quedas de telefone', mudanças de bateria, falhas de hardware, atualizações de sistema operacional, reinicializações em geral, etc., de modo que o estado persiste quando salvo em memória não volátil.

@jsroest Seus argumentos são válidos, mas IMHO não se aplica a este problema em particular, uma vez que o "estado" aludido aqui é o estado transitório da interface do usuário (por exemplo, posição de rolagem, seleção (ões) ativa (s), entrada de texto temporário, etc ...). Lembre-se de que restaurar esse tipo de dados só é necessário nas situações em que o usuário não descartou explicitamente a "Tela", mas a entrada do usuário ainda pode se perder, como quando um usuário muda para outro aplicativo. Não há expectativa do usuário para recuperar o estado transitório após a perda de energia, da mesma forma que não há expectativa do usuário de que o navegador role a última página da Web exibida para o deslocamento exato antes de uma perda de energia do sistema. Sendo assim, a natureza dos dados considerados como "estado transiente" é significativa e a discussão aqui (a facilidade Android onSaveInsatnceState em particular) é sobre como lidar com esse tipo de dados.

@gitspeak
Eu escrevo programas para empresas. Por exemplo, programas que são usados ​​em depósitos por selecionadores de pedidos ou outro exemplo no negócio de varejo, onde o dono da loja escaneia os itens para fazer o pedido de seu fornecedor. Meus clientes esperam que o aplicativo comece de onde o deixaram, não importa o que aconteça. Isso inclui o estado transitório, mas também a pilha de navegação. Você poderia explicar por que isso não é um ajuste perfeito para o recurso OnSaveInstanceState conforme discutido aqui? Talvez a expectativa do usuário no mercado consumidor seja diferente, mas a mesma lógica para salvar e restaurar o estado pode ser usado IMHO.

@jsroest

Meus clientes esperam que o aplicativo comece de onde o deixaram, não importa o que aconteça.

Desculpe, não sei como construir aplicativos para esses clientes, talvez alguém possa ajudar ...

@jsroest Essa é uma ótima pergunta, mas não está no escopo deste problema. A flutuação é limitada pelo que a própria plataforma pode fazer e, como @gitspeak menciona, o onSaveInstanceState do Android trata de salvar o estado transitório da IU em que o ciclo de vida dos dados é diferente do que você deseja. O armazenamento local ou remoto (se você quiser que o estado sobreviva após desinstalações e dispositivos) fornecerá o que você deseja, mas tem ressalvas, que estão fora do escopo deste problema.

Leitura adicional recomendada:

https://developer.android.com/topic/libraries/architecture/saving-states [Observe, por exemplo, como este artigo separa o armazenamento local de onSaveInstanceState]
https://developer.android.com/guide/topics/data/data-storage
https://firebase.google.com/docs/firestore/

@jsroest se seus clientes esperam que o aplicativo retorne de onde estava, mesmo que tenha sido interrompido à força, tarefa limpa ou o sistema operacional tenha sido reiniciado , isso é algo totalmente personalizado, e não sobre o que as pessoas estão falando aqui.

onSaveInstanceState estava salvando o estado da tarefa para uma determinada tarefa, mas foi descartado na limpeza da tarefa, parada forçada ou reinicialização do sistema operacional.

@mehmetf Acho que esse problema realmente atingiu um nível de verbosidade no qual pode ser bastante desafiador grocar. Com a bênção de @LouisCAD , sugiro que você feche este número e abra um novo que comece com seu resumo mais recente (talvez um pouco adaptado para enfatizar a definição e o escopo do problema) junto com referências anteriores a discussões anteriores.

Concordo, mas deixarei isso para @matthew-carroll; ele provavelmente terá esse problema.

Obrigado pelo feedback.

Não quero forçar as coisas, mas acho que não fui suficientemente claro ao descrever o que estou procurando. Não estou à altura de toda a terminologia usada aqui neste tópico. Vejo muitas semelhanças entre o que gostaria de ver no Flutter e o que @LouisCAD e @passsy estão descrevendo.

Meus programas geralmente consistem nas seguintes partes:

  • Back-end para enviar dados do estado estável quando o usuário terminar um trabalho.
  • Banco de dados sqlite local para o estado estacionário
  • Na memória, estrutura de dados chamada de 'variáveis' para o estado transiente que pode ser serializada em memória não volátil para que possa sobreviver ao processo.

A estrutura de dados de 'variáveis' possui apenas classes simples com propriedades que são serializáveis. Uma parte importante dessa estrutura de dados de 'variáveis' é a pilha de telas. (Simplesmente Listar) A tela que fica no topo é a última tela com a qual o usuário interagiu. O abaixo é aquele para o qual o usuário navega ao pressionar 'voltar'.

Posso imaginar que OnSaveInstanceState pode ser usado na plataforma Android para acionar uma serialização dessa estrutura de dados. Algo semelhante deve ser encontrado nas outras plataformas (iOS, futuro: win, mac, web), mas como @passy sugere, outros pontos-chave também podem desencadear isso e isso pode ser bom o suficiente.

Quando o programa for iniciado, ele irá verificar se uma estrutura de dados serializada está disponível e se a versão atual do programa é a mesma que a versão que o serializou. Isso pode ser feito apenas comparando os números de versão. Não queremos carregar uma estrutura de dados que não seja compatível.

Se tudo isso somar, a estrutura de dados é desserializada e fica disponível na memória. O programa carrega a tela que está no topo da pilha. A própria tela carrega seu estado transitório da estrutura de dados. Agora temos um programa que sobrevive às mortes em processos. (Ou estou perdendo alguma coisa? Isso definitivamente funciona em meus projetos anteriores no Windows mobile, formulários Xamarin e asp.net. Acho que também deve funcionar no Flutter).

Aplicativo de exemplo, em que SXXX representa uma tela com um número. O número é usado apenas para lembrar ao programador quais telas pertencem mais ou menos umas às outras.

S100 Main menu
  S200 Order pick
    S210 Select order
      S220 Confirm pick location
        S230 Pick nr of pieces
          S240 Report shortage
        S250 Scan dropoff location
    S300 Cycle count
      S210 XXXXXX
        S220 XXXXXX
      S230 XXXXXX
    S300 Reprint labels
      S310 XXXXXX

Estrutura de dados de variáveis ​​de amostra

Class variables {
    Class AmbientVariables {
        ….
    }

    Class ScreenStack{
        List<string> ScreenStack;
    }

    Class Orderpick {
        int selected_order;
        string comfirmed_location;
        string nr_picked;
        ….
    }

    Class CycleCount {
        ….
    }

    Class ReprintLabels {
        ….
    }
}

Tudo o que eu gostaria de ver em vibração provavelmente já está lá, exceto a recriação da pilha de telas, também conhecida como pilha de navegação. Não tenho ideia de como recriar essa árvore de objetos na memória. Também não tenho certeza se devo querer recriá-lo. Também posso criar uma pilha de navegação própria e implementá-la em flutter como um aplicativo de página única. O mesmo que fiz em projetos anteriores. Mas então eu perderia muitos recursos internos das classes MaterialApp e Scaffold, onde a seta / botão para trás é o mais óbvio.

Eu percebo que isso não salva automaticamente todos os estados transitórios. Por exemplo, textos selecionados, posição de listas, posição de uma animação específica. Mas, como programador, você pode decidir por tela o que é necessário salvar. Embora seja exatamente isso que @LouisCAD está tentando evitar, porque todo o código clichê é necessário.

Devo fazer um novo problema ou isso se encaixa neste tópico?

Obrigado novamente pelo feedback, eu realmente aprecio isso.

@jsroest Obrigado pela explicação detalhada. Poste isso no StackOverflow com a tag flutter. Você está essencialmente pedindo uma recomendação sobre como estruturar e construir suas rotas e como salvar a última rota visitada no armazenamento persistente.

Você pode pensar que resolver esse problema específico resolve o seu problema também, mas isso não é verdade. Você pode me mandar uma mensagem no meu github para @ gmail.com se não entender o porquê.

Algum progresso neste assunto?

@vanlooverenkoen teoricamente se você persistir a lista de rotas do navegador E a hierarquia de chaves para um arquivo (e o estado dos widgets com estado) e limpar quando no lado nativo do Android você obtiver savedInstanceState == null , então você poderia potencialmente fazer isso persistente e restaurável, não é tão fácil.

@Zhuinden Na verdade, é muito mais complexo do que salvar as rotas e as chaves. A comunicação entre as telas é feita usando callbacks ou aguardando um resultado. De alguma forma, você também teria que restaurar esses estados. Caso contrário, você acaba tendo algo que parece correto, mas não age como tal. Você poderia contornar a restauração de retornos de chamada e outras coisas usando uma forma de comunicação usando pacotes de dados, assim como você faz usando o Android nativo (com intenções), mas isso tiraria muito da facilidade de uso do Flutter. Enquanto não houver uma solução real para esse problema, o Flutter não pode ser usado por muitos aplicativos, principalmente aqueles usados ​​para coletar dados grandes e complexos baseados em formulários. Eu me pergunto por que esse problema não é abordado abertamente pela equipe do Flutter, porque eles certamente devem estar cientes disso. Talvez revelasse uma falha séria na arquitetura do Flutter e mostrasse que, além de apresentações chamativas para impressionar a administração, o sistema não é uma boa opção para aplicações da vida real.

@jsroest @mehmetf @claudiofelber , a resposta a esta pergunta StackOverflow aborda esses problemas? https://stackoverflow.com/questions/54015775/what-is-the-best-way-to-pass-data-or-arguments-between-flutter-app-screens/54015932

Eu tenho acompanhado isso desde o início, mas tive que reler um monte de mensagens porque perdi o contexto.

Se a equipe do Flutter acredita que deve haver um problema novo, fresco, com menos inundação e planos mais claros, não tenho nenhum problema com isso e este sendo encerrado como resultado.

Eu criei o plugin native_state para alavancar os mecanismos Android e iOS para salvar o estado do aplicativo quando o processo é encerrado.

O Navigator já pode restaurar as rotas do aplicativo por meio da propriedade initialRoute , como se constatou, _se_ você seguir algumas regras específicas, que eu documentei e adicionei exemplos para, e provavelmente sempre quer usar o plugin em combinação com a restauração da rota (usando o SavedStateRouteObserver ou algum outro mecanismo).

Tao acabou de me dizer que este foi o quarto problema mais solicitado em nossa pesquisa no outono passado. Acho que há uma mistura de desejos do usuário expressos aqui (nos mais de 100 comentários!) E precisaremos nos sentar em algum momento e separá-los em itens separados nos quais possamos agir.

@eseidelGoogle Não concordo com sua avaliação. IMHO, não há uma “mistura de desejos do usuário expressos”, mas sim a falta de suporte da plataforma Android, que apresenta diferentes desafios de programação resultantes do mesmo problema central.

Os aplicativos Android têm dois temas centrais:

1) Máquina de estado de processo (também conhecida como eventos de ciclo de vida).
2) Kit de ferramentas de IU.

Flutter faz (2), mas não (1). Precisa abordar ambos.

3 anos...

Atualização rápida: Atualmente, estou trabalhando ativamente na exploração de soluções para este problema.

@goderbauer Apenas para garantir que este não é apenas um problema do Android, o iOS possui os mesmos mecanismos, embora o impacto nos aplicativos iOS seja provavelmente menor. Eu criei um plugin que permite salvar o estado por meio dos mecanismos do sistema operacional que pode ser algo para se olhar também: https://github.com/littlerobots/flutter-native-state

@hvisser como você recomenda salvar o estado de navegação? O Navigator from Flutter não tem um método getRoutes .

@hvisser Sim, estou analisando isso para ambas as plataformas (e deve ser flexível o suficiente para se conectar a outras plataformas também). Removi o rótulo plataforma-android para deixar claro que este não é um problema específico do Android.

@hvisser como você recomenda salvar o estado de navegação? O Navigator from Flutter não tem um método getRoutes .

@Zhuinden O navegador já tem suporte para ele (embora eu não ache que seja muito conhecido). Eu tenho um exemplo de como configurá-lo no projeto do estado nativo, mas TL; DR

  • Você deve usar rotas nomeadas
  • As rotas devem ser hierárquicas, por exemplo, /screen1/screen2 segue em /screen1 segue em /
  • As rotas são enviadas para a pilha de navegação quando você define a propriedade initialRoutes e suas rotas são organizadas dessa forma
  • _Penso_ que você também precisa de GlobalKey para navigatorKey em MaterialApp mas isso pode ser específico para o meu caso.

Portanto, se você definir initialRoutes como /screen1/screen2 e tiver suas rotas configuradas corretamente, a restauração da navegação também funcionará.

Mais um caso em dispositivos antigos com 2 cartões SIM quando você liga e desliga o modo avião no youtube nativo e os aplicativos de flutter serão reiniciados # 49057
O vídeo está anexado ao meu problema

Tente desativar o modo de depuração de flutter. se o modo de depuração estiver habilitado, o aplicativo sempre será reiniciado.

debugShowCheckedModeBanner: false,

Trabalho com flutter desde 2018, talvez junho / julho, e descobri que isso era um problema depois de 2 a 3 meses. Eu não sabia que isso se tornava um grande problema. Eu estava fazendo um aplicativo de player de música e, se pressionasse o botão Voltar, a vibração costumava destruir todos os estados enquanto a música continuava tocando ao fundo. Para resolver isso, eu fiz esta biblioteca system_shortcuts .
Adicione o plugin ao seu projeto em pubspec.yaml _ [Para aqueles que são iniciantes :)] _

dependencies:
  flutter:
    sdk: flutter

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.2
  system_shortcuts:

Sempre usei uma

Uso -

Qualquer que seja o widget em que você esteja e ao pressionar o botão Voltar, você sabe que o aplicativo será fechado (ou seja, o usuário sai do aplicativo), adicione WidgetsBindingObserver Mixin na classe de estado como este

class _MusicPlayerState extends State<MusicPlayer>
    with WidgetsBindingObserver{
...
}

Depois de fazer isso, substitua o método didPopRoute e adicione o seguinte código no método

<strong i="21">@override</strong>
  Future<bool> didPopRoute() async {
    await SystemShortcuts.home();
  return true;
  }

_Vamos entender como isso funciona_:
O aplicativo está em execução >> O usuário pressiona o botão Voltar >> O Início é pressionado e o botão Voltar não é >> O aplicativo vai para o fundo e o estado não é destruído NENHUM DELE

Amostra de Trabalho -

_ EDITAR _
_Este aplicativo foi escrito usando o gerenciamento de estado do provedor, mas não há nenhum código usado para gerenciar qualquer estado após o fechamento do aplicativo e a reabertura do recente. É apenas o código que adicionamos trabalhando aqui._

ezgif com-video-to-gif

ezgif com-video-to-gif (1)

Problemas -

Se você acessou seu aplicativo de outro aplicativo, sempre que voltar de seu widget inicial, você sempre irá para a tela inicial e não para o aplicativo anterior de onde veio.
Exemplo :-
O usuário está no WhatsApp (ou qualquer outro aplicativo) >> Eles recebem uma notificação do seu aplicativo >> Eles abrem o seu aplicativo a partir da notificação >> Agora eles pressionam o botão Voltar >> Eles vão voltar para a tela inicial do telefone e não WhatsApp (que deve ser o cenário padrão)
_Esse problema não me causou nenhum grande problema até agora e acho que todos os usuários e desenvolvedores normais não terão problemas com isso, pois em casos raros você acessa seu aplicativo de outro aplicativo. Não estou negligenciando que isso pode acontecer silenciosamente algumas vezes, mas funciona para mim e é apenas uma solução alternativa até que obtenha ajuda real da equipe central do flutter._

O fato desse problema ter quase 3 anos e meio e ainda assim nada é tão preocupante.

Aparentemente, isso deve ser resolvido em maio de 2020, a julgar pelo marco atualmente definido ... a tempo para o Google I / O?

O hvisser acima resolveu o problema, agora as pessoas só precisam escrever o código que o faz funcionar

Estou progredindo no sentido de fornecer uma solução para isso. Para uma prévia de como um aplicativo será capaz de restaurar o estado da instância depois que isso for feito, verifique este PR: https://github.com/flutter/samples/pull/433.

Infelizmente, vai demorar um pouco mais até que esteja tudo pronto.

O documento de design com detalhes sobre como fazer a restauração do estado no Flutter está disponível aqui: http://flutter.dev/go/state-restoration-design

O PR com a estrutura geral de restauração do estado, conforme descrito no design acima, foi postado: https://github.com/flutter/flutter/pull/60375. Ainda estou trabalhando para adicionar mais cobertura de teste.

A estrutura geral de restauração de estado (https://github.com/flutter/flutter/pull/60375) foi enviada. Agora estou trabalhando para integrá-lo em nossos widgets. Vou começar com roláveis, campos de texto e o Navegador.

Você mesmo pode implementar isso armazenando os dados que deseja restaurar dentro de um banco de dados sqlite . Essa é uma técnica usada ao escrever servidores.

Isso tem a vantagem de restaurar um aplicativo mesmo que a bateria do telefone acabe ou haja um kernel panic ou qualquer outra coisa. Eu recomendo fazer um wrapper OO em torno de suas chamadas SQLite. O Dart não tem algo como SQLite.Net que faz isso automaticamente para você, infelizmente.

editar: também é algo que fizemos nos primeiros dias do desenvolvimento móvel porque a memória era muito limitada e o sqlite é uma ótima maneira de trocar coisas entre a memória e o disco.

Estou encerrando este problema desde que a estrutura de restauração de estado geral (e a incorporação do Android) foi lançada. O trabalho restante é rastreado em edições separadas:

Bem feito!!!!! Obrigado vibração !!!

Esta página foi útil?
0 / 5 - 0 avaliações