Tutorial: Atlas Device Sync para Flutter
Nesta página
- Objetivos de aprendizagem
- Pré-requisitos
- Comece com o Modelo
- Configurar o Aplicativo Modelo
- Abra o aplicativo
- Explore a estrutura do aplicativo
- Execute o aplicativo
- Verificar o Backend
- Modificar a Aplicação
- Adicionar uma nova propriedade
- Adicionar uma nova propriedade ao modelo
- Definir a prioridade ao criar e atualizar itens
- Executar e testar
- Alterar a assinatura
- Atualizar a assinatura
- Executar e testar
- Conclusão
- O que vem a seguir?
Tempo estimado para conclusão: 30 minutos, dependendo da sua experiência com o Flutter
O Atlas Device SDK for Flutter permite criar aplicativos de várias plataformas com o Dart e o Flutter. Este tutorial baseia-se no aplicativo Flutter Flexible Sync Template App, denominado flutter.todo.flex
, que ilustra a criação de um aplicação Todo. Este aplicação permite que o usuário:
Registre seu e-mail como uma nova conta de usuário.
Faça login na conta deles com o e-mail e a senha (e saia mais tarde).
Visualize, crie, modifique e exclua itens de tarefa.
Visualizar todas as tarefas, mesmo onde o usuário não é o proprietário.
O aplicativo de modelo também oferece uma alternância que simula o dispositivo no "Modo Offline". Essa alternância permite que você teste rapidamente a Device Sync, emulando o usuário que não tem conexão com a internet. No entanto, você provavelmente removeria essa opção em um aplicativo de produção.
Este tutorial é adicionado ao aplicativo de modelo. Você adicionará um novo campo priority
ao modelo de Item
existente e atualizará a inscrição da Flexible Sync para mostrar apenas itens dentro de uma faixa de priorities.
Objetivos de aprendizagem
Este tutorial ilustra como você pode adaptar o aplicativo modelo para suas próprias necessidades. Você não faria necessariamente esta alteração dada a estrutura atual do aplicativo modelo.
Neste tutorial, você aprenderá como:
Atualize um modelo de objeto Realm com uma alteração não inovadora.
Atualizar uma inscrição do Device Sync
Adicione um campo de query à configuração do Device Sync no servidor para alterar quais dados são sincronizados.
Se você preferir começar com seu próprio aplicação em vez de seguir um tutorial guiado, confira o Flutter Quick Start. Ele inclui exemplos de código copiáveis e as informações essenciais de que você precisa para configurar um aplicação Flutter SDK .
Pré-requisitos
Você deve ter experiência anterior na implantação de aplicativos Flutter para um emulador Android, simulador iOS e/ou um dispositivo físico.
Este tutorial começa com um aplicativo de modelo. Você precisa de uma conta do Atlas, de uma chave de API e do App Services CLI para criar um aplicativo modelo.
Você pode aprender mais sobre a criação de uma conta do Atlas na documentação de Comece a usar o Atlas. Para este tutorial, você precisa de uma conta do Atlas com um cluster de camada livre.
Você também precisa de uma chave de API do Atlas para a conta do MongoDB Cloud com a qual deseja se conectar. Você deve ser proprietário do projeto para criar um aplicativo modelo usando o App Services CLI.
Para saber mais sobre como instalar o App Services CLI, consulte Instalar o App Services CLI. Após instalar, execute o comando login utilizando a chave API para seu projeto Atlas.
Observação
Plataformas suportadas
Você pode criar esse aplicativo tutorial nas seguintes plataformas:
iOS
Android
macOS
Windows
Linux
O Flutter SDK não oferece suporte à criação de aplicativos da web.
Comece com o Modelo
Este tutorial baseia-se no aplicativo do modelo da Sincronização flexível do Flutter denominado flutter.todo.flex
. Começamos com o aplicativo padrão e construímos novos recursos nele.
Para saber mais sobre os aplicativos de modelo, consulte Aplicativos de modelo.
Se você ainda não tiver uma conta do Atlas, cadastre-se para implantar um Aplicativo Modelo.
Siga o procedimento descrito no guia Crie uma App Services App e selecione Create App from Template. Selecione o modelo Real-time Sync . Isso cria um App Services App pré-configurado para uso com um dos clientes da aplicação do modelo do Device Sync .
Após criar um aplicativo de modelo, a interface do usuário exibe um modal rotulado Get the Front-end Code for your Template. Esse modal fornece instruções para baixar o código do cliente do aplicativo modelo como um arquivo .zip
ou usar a CLI do App Services para obter o cliente.
Após selecionar o método CLI do .zip
ou App Services, siga as instruções na tela para obter o código do cliente. Para este tutorial, selecione o código de cliente Dart (Flutter).
Descompacte o aplicativo baixado e você verá o aplicativo Flutter.
Observação
O utilitário ZIP padrão do Windows pode mostrar o arquivo .zip arquivo como vazio. Se você encontrar isso, use um dos programas de compactação de terceiros disponíveis.
O comando appservices apps create configura o back-end e cria um aplicativo modelo Flutter para você usar como base para este tutorial.
Execute o seguinte comando em uma janela de terminal para criar um aplicativo chamado "MyTutorialApp" que é implantado na região US-VA
com seu ambiente definido como "desenvolvimento" (em vez de produção ou controle de qualidade).
appservices app create \ --name MyTutorialApp \ --template flutter.todo.flex \ --deployment-model global \ --environment development
O comando cria um novo diretório no caminho atual com o mesmo nome do valor do sinalizador --name
.
Você pode bifurcar e clonar um repositório do Github que contenha o código do cliente Device Sync . O código do cliente Flutter está disponível em https://github.com/mongodb/template-app-dart-flutter-todo.
Se você usar esse processo para obter o código do cliente, deverá criar um aplicativo modelo para usar com o cliente. Siga as instruções em Crie um aplicativo modelo para usar a interface do usuário do Atlas App Services, App Services CLI ou a Admin API para criar um aplicativo modelo de backend do Device Sync.
Clone o repositório e siga as instruções em README
para adicionar o ID do aplicativo de back-end ao aplicativo Flutter.
Configurar o Aplicativo Modelo
Abra o aplicativo
Abra o aplicativo Flutter com seu editor de código.
Se você baixou o cliente como um arquivo .zip
ou clonou o repositório GitHub do cliente, deverá inserir manualmente a ID do Aplicativo de Serviços de Aplicativo no local apropriado em seu cliente. Siga as instruções do Configuration no cliente README.md
para saber onde inserir sua ID do aplicativo.
Explore a estrutura do aplicativo
No editor de código, dedique alguns minutos para explorar como o projeto está organizado. Este é um aplicação Flutter multiplataforma padrão modificado para nosso uso específico. Especificamente, os seguintes arquivos contêm usos importantes do Flutter SDK:
arquivo | Propósito |
---|---|
lib/main.dart | Ponto de entrada no aplicativo. Contém roteamento e gerenciamento de estado. |
lib/realm/schemas.dart | Define o esquema do Realm Database. |
lib/realm/schemas.realm.dart | Classe de objeto de Realm gerada. |
lib/realm/app_services.dart | Lida com a interação com o Atlas App Services. |
lib/realm/realm_services.dart | Lida com a interação com Realm Database e Atlas Device Sync. |
lib/components/ | Partes componentes do aplicativo com widgets Flutter. |
lib/screens/ | Páginas do aplicativo. |
Execute o aplicativo
Sem fazer alterações no código, você deve ser capaz de executar o aplicativo no emulador Android, simulador iOS, dispositivo móvel físico ou emulador de desktop.
Primeiro, instale todas as dependências executando o seguinte em seu terminal:
flutter pub get
Em seguida, conecte a um dispositivo e execute o aplicativo Flutter.
Quando o aplicativo estiver em execução, registre uma nova conta de usuário e, em seguida, adicione um novo item à sua lista de tarefas.
Dica
Para obter mais informações sobre como executar um aplicativo Flutter com ferramentas de desenvolvimento, consulte a documentação do Flutter Test Drive.
Verificar o Backend
Conecte-se ao MongoDB Atlas. Na aba Data Services, clique em Browse Collections. Na lista de bancos de dados, localize e expanda o banco de dados do todo e, em seguida, a coleção do Item. Você deverá ver o documento que criou nesta coleção.
Modificar a Aplicação
Adicionar uma nova propriedade
Adicionar uma nova propriedade ao modelo
Agora que você confirmou que tudo está funcionando como o esperado, podemos adicionar alterações. Neste tutorial, decidimos que queremos adicionar uma propriedade "priority" a cada Item para que possamos filtrar os Itens por suas prioridades.
Para fazer isso, siga estas etapas:
No projeto
flutter.todo.flex
, abra o arquivolib/realm/schemas.dart
.Adicione a seguinte propriedade à classe
_Item
:lib/realm/schemas.dartlate int? priority; Gere novamente a classe de Objeto de Realm
Item
:dart run realm generate
Definir a prioridade ao criar e atualizar itens
Em
lib/realm/realm_services.dart
, adicione lógica para definir e atualizarpriority
. Adicione também uma classe abstrataPriorityLevel
abaixo da classeRealmServices
para restringir os valores possíveis.lib/realm/realm_services.dart// ... imports class RealmServices with ChangeNotifier { static const String queryAllName = "getAllItemsSubscription"; static const String queryMyItemsName = "getMyItemsSubscription"; bool showAll = false; bool offlineModeOn = false; bool isWaiting = false; late Realm realm; User? currentUser; App app; // ... RealmServices initializer and updateSubscriptions(), // sessionSwitch() and switchSubscription() methods void createItem(String summary, bool isComplete, int? priority) { final newItem = Item(ObjectId(), summary, currentUser!.id, isComplete: isComplete, priority: priority); realm.write<Item>(() => realm.add<Item>(newItem)); notifyListeners(); } void deleteItem(Item item) { realm.write(() => realm.delete(item)); notifyListeners(); } Future<void> updateItem(Item item, {String? summary, bool? isComplete, int? priority}) async { realm.write(() { if (summary != null) { item.summary = summary; } if (isComplete != null) { item.isComplete = isComplete; } if (priority != null) { item.priority = priority; } }); notifyListeners(); } Future<void> close() async { if (currentUser != null) { await currentUser?.logOut(); currentUser = null; } realm.close(); } void dispose() { realm.close(); super.dispose(); } } abstract class PriorityLevel { static int severe = 0; static int high = 1; static int medium = 2; static int low = 3; } Adicione um novo arquivo para conter um widget para definir a prioridade de um item. Crie o arquivo
lib/components/select_priority.dart
.lib/components/select_priority.dartimport 'package:flutter/material.dart'; import 'package:flutter_todo/realm/realm_services.dart'; class SelectPriority extends StatefulWidget { int priority; void Function(int priority) setFormPriority; SelectPriority(this.priority, this.setFormPriority, {Key? key}) : super(key: key); State<SelectPriority> createState() => _SelectPriorityState(); } class _SelectPriorityState extends State<SelectPriority> { Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.only(top: 15), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('Priority'), DropdownButtonFormField<int>( onChanged: ((int? value) { final valueOrDefault = value ?? PriorityLevel.low; widget.setFormPriority(valueOrDefault); setState(() { widget.priority = valueOrDefault; }); }), value: widget.priority, items: [ DropdownMenuItem( value: PriorityLevel.low, child: const Text("Low")), DropdownMenuItem( value: PriorityLevel.medium, child: const Text("Medium")), DropdownMenuItem( value: PriorityLevel.high, child: const Text("High")), DropdownMenuItem( value: PriorityLevel.severe, child: const Text("Severe")), ], ), ], ), ); } } Agora adicione o widget
SelectPriority
aos widgetsCreateItem
eModifyItem
. Você também precisa incluir alguma lógica adicional para lidar com a configuração da prioridade. O código que você deve adicionar é mostrado abaixo.Algumas seções dos arquivos que você está adicionando são substituídos por comentários nos exemplos de código abaixo para se concentrarem nas seções de código alteradas.
Edite o widget
CreateItemForm
emlib/components/create_item.dart
:lib/components/create_item.dart// ... other imports import 'package:flutter_todo/components/select_priority.dart'; // ... CreateItemAction widget class CreateItemForm extends StatefulWidget { const CreateItemForm({Key? key}) : super(key: key); createState() => _CreateItemFormState(); } class _CreateItemFormState extends State<CreateItemForm> { final _formKey = GlobalKey<FormState>(); late TextEditingController _itemEditingController; int _priority = PriorityLevel.low; void _setPriority(int priority) { setState(() { _priority = priority; }); } // ... initState() and dispose() @override functions Widget build(BuildContext context) { TextTheme theme = Theme.of(context).textTheme; return formLayout( context, Form( key: _formKey, child: Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: <Widget>[ // ... Text and TextFormField widgets SelectPriority(_priority, _setPriority), // ... Padding widget ], ), )); } void save(RealmServices realmServices, BuildContext context) { if (_formKey.currentState!.validate()) { final summary = _itemEditingController.text; realmServices.createItem(summary, false, _priority); Navigator.pop(context); } } } Edite o widget
ModifyItemForm
emlib/components/modify_item.dart
:lib/components/modify_item.dart// ... other imports import 'package:flutter_todo/components/select_priority.dart'; class ModifyItemForm extends StatefulWidget { final Item item; const ModifyItemForm(this.item, {Key? key}) : super(key: key); _ModifyItemFormState createState() => _ModifyItemFormState(item); } class _ModifyItemFormState extends State<ModifyItemForm> { final _formKey = GlobalKey<FormState>(); final Item item; late TextEditingController _summaryController; late ValueNotifier<bool> _isCompleteController; late int? _priority; void _setPriority(int priority) { setState(() { _priority = priority; }); } _ModifyItemFormState(this.item); void initState() { _summaryController = TextEditingController(text: item.summary); _isCompleteController = ValueNotifier<bool>(item.isComplete) ..addListener(() => setState(() {})); _priority = widget.item.priority; super.initState(); } void dispose() { _summaryController.dispose(); _isCompleteController.dispose(); super.dispose(); } Widget build(BuildContext context) { TextTheme myTextTheme = Theme.of(context).textTheme; final realmServices = Provider.of<RealmServices>(context, listen: false); return formLayout( context, Form( key: _formKey, child: Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: <Widget>[ // ... Text and TextFormField widgets SelectPriority(_priority ?? PriorityLevel.medium, _setPriority), // ... StatefulBuilder widget Padding( padding: const EdgeInsets.only(top: 15), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ cancelButton(context), deleteButton(context, onPressed: () => delete(realmServices, item, context)), okButton(context, "Update", onPressed: () async => await update( context, realmServices, item, _summaryController.text, _isCompleteController.value, _priority)), ], ), ), ], ))); } Future<void> update(BuildContext context, RealmServices realmServices, Item item, String summary, bool isComplete, int? priority) async { if (_formKey.currentState!.validate()) { await realmServices.updateItem(item, summary: summary, isComplete: isComplete, priority: priority); Navigator.pop(context); } } void delete(RealmServices realmServices, Item item, BuildContext context) { realmServices.deleteItem(item); Navigator.pop(context); } } Agora adicione um indicador visual para prioridade no widget
ItemCard
nolib/components/todo_item.dart
. Cria um novo widget_PriorityIndicator
que fornece um indicador visual da prioridade do item.Adicione um widget
_PriorityIndicator
que você acabou de criar no widgetTodoItem
.lib/components/todo_item.dart// ... imports enum MenuOption { edit, delete } class TodoItem extends StatelessWidget { final Item item; const TodoItem(this.item, {Key? key}) : super(key: key); Widget build(BuildContext context) { final realmServices = Provider.of<RealmServices>(context); bool isMine = (item.ownerId == realmServices.currentUser?.id); return item.isValid ? ListTile( // ... leading property and child content title: Row( children: [ Padding( padding: const EdgeInsets.only(right: 8.0), child: _PriorityIndicator(item.priority), ), SizedBox(width: 175, child: Text(item.summary)), ], ), // ... subtitle, trailing, and shape properties with child content ) : Container(); } // ... handleMenuClick() function } class _PriorityIndicator extends StatelessWidget { final int? priority; const _PriorityIndicator(this.priority, {Key? key}) : super(key: key); Widget getIconForPriority(int? priority) { if (priority == PriorityLevel.low) { return const Icon(Icons.keyboard_arrow_down, color: Colors.blue); } else if (priority == PriorityLevel.medium) { return const Icon(Icons.circle, color: Colors.grey); } else if (priority == PriorityLevel.high) { return const Icon(Icons.keyboard_arrow_up, color: Colors.orange); } else if (priority == PriorityLevel.severe) { return const Icon( Icons.block, color: Colors.red, ); } else { return const SizedBox.shrink(); } } Widget build(BuildContext context) { return getIconForPriority(priority); } }
Executar e testar
Antes de executar o aplicativo novamente, execute uma reinicialização a quente. Isso garante que a sessão de sincronização seja reiniciada com o novo esquema e evita erros de sincronização.
Em seguida, faça login usando a conta que você criou neste tutorial. Você verá o item que você criou anteriormente. Adicione um novo item e você verá que agora você pode definir a prioridade. Escolha High
para a prioridade e salve o item.
Agora volte para a página de dados do Atlas em seu navegador e atualize a coleção do Item
. Você deve agora visualizar o novo Item com o campo priority
adicionado e configurado para 1. Você também notará que o Item existente agora também tem um campo priority
e está configurado para nulo, como mostrado na seguinte captura de tela:
Observação
Por que isso não interrompeu a sincronização?
A adição de uma propriedade a um objeto Realm não é uma alteração de ruptura e, portanto, não exige uma reinicialização do cliente. O aplicativo de modelo tem o Modo de Desenvolvimento habilitado, portanto, as alterações no objeto Realm do cliente são refletidas no esquema do lado do servidor. Para obter mais informações, consulte Modo de desenvolvimento e Atualizar seu modelo de dados.
Alterar a assinatura
Agora que adicionamos o campo de prioridade, queremos atualizar a assinatura do Device Sync para sincronizar somente Itens marcados como prioridade Alta ou Severa.
Atualizar a assinatura
No arquivo lib/realm/realm_services.dart
, definimos a inscrição da Sincronização flexível que define quais documentos sincronizamos com o dispositivo e a conta do usuário. No momento, estamos sincronizando todos os documentos em que a propriedade owner
corresponde ao usuário autenticado.
A inscrição atual:
Future<void> updateSubscriptions() async { realm.subscriptions.update((mutableSubscriptions) { mutableSubscriptions.clear(); if (showAll) { mutableSubscriptions.add(realm.all<Item>(), name: queryAllName); } else { mutableSubscriptions.add( realm.query<Item>(r'owner_id == $0', [currentUser?.id]), name: queryMyItemsName); } }); await realm.subscriptions.waitForSynchronization(); }
Agora, vamos alterar a inscrição para sincronizar apenas itens de priority alta e grave. Como você deve se lembrar, o campo de priority é do tipo int
, em que a priority mais alta ("Severe") tem o valor 0 e a priority mais baixa ("Low") tem o valor 3.
Podemos fazer comparações diretas entre um int e a propriedade prioritária. Para isso, vamos refatorar a consulta de inscrição para incluir itens em que a priority seja menor ou igual a PriorityLevel.high
(ou 1). Também daremos à inscrição o novo nome "getMyHighPriorityItemsSubscription"
.
Atualize a assinatura para excluir a assinatura antiga e adicionar uma nova que use prioridade:
// ... imports class RealmServices with ChangeNotifier { static const String queryAllName = "getAllItemsSubscription"; static const String queryMyItemsName = "getMyItemsSubscription"; static const String queryMyHighPriorityItemsName = "getMyHighPriorityItemsSubscription"; bool showAll = false; bool offlineModeOn = false; bool isWaiting = false; late Realm realm; User? currentUser; App app; RealmServices(this.app) { if (app.currentUser != null || currentUser != app.currentUser) { currentUser ??= app.currentUser; realm = Realm(Configuration.flexibleSync(currentUser!, [Item.schema])); showAll = (realm.subscriptions.findByName(queryAllName) != null); // Check if subscription previously exists on the realm final subscriptionDoesNotExists = (realm.subscriptions.findByName(queryMyHighPriorityItemsName) == null); if (realm.subscriptions.isEmpty || subscriptionDoesNotExists) { updateSubscriptions(); } } } Future<void> updateSubscriptions() async { realm.subscriptions.update((mutableSubscriptions) { mutableSubscriptions.clear(); if (showAll) { mutableSubscriptions.add(realm.all<Item>(), name: queryAllName); } else { mutableSubscriptions.add( realm.query<Item>( r'owner_id == $0 AND priority <= $1', [currentUser?.id, PriorityLevel.high], ), name: queryMyHighPriorityItemsName); } }); await realm.subscriptions.waitForSynchronization(); } // ... other methods }
Executar e testar
Execute o aplicativo novamente. Faça login usando a conta que você criou neste tutorial.
Após um momento inicial, quando o Realm ressincroniza a coleção de documentos, você pode ver uma mensagem de erro semelhante à seguinte:
The following RangeError was thrown building StreamBuilder<RealmResultsChanges<Item>>(dirty, state: _StreamBuilderBaseState<RealmResultsChanges<Item>, AsyncSnapshot<RealmResultsChanges<Item>>>#387c4): RangeError (index): Invalid value: Only valid value is 0: 3
Este erro pode ocorrer com o widget StreamBuilder
como a assinatura atualiza. Em um app de produção, você pode adicionar o tratamento de erros. Mas, para fins deste tutorial, basta realizar uma atualização rápida e o erro desaparecerá.
Agora você deve ver o novo Item de Alta priority que você criou.
Dica
Alterando assinaturas com o modo de desenvolvedor ativado
Neste tutorial, quando você altera a assinatura e a query no campo de prioridade pela primeira vez, o campo é adicionado automaticamente ao Device Sync Collection Queryable Fields. Isso ocorre porque o aplicativo de modelo tem o Modo de Desenvolvimento habilitado por padrão. Se o Modo de Desenvolvimento não estivesse habilitado, você teria que adicionar manualmente o campo como um campo consultável para usá-lo em uma query de sincronização do lado do cliente.
Para obter mais informações, consulte Campos consultáveis.
Se quiser testar ainda mais a funcionalidade, crie Itens de várias prioridades. Você verá um novo item com uma prioridade mais baixa aparecer brevemente na lista de itens e depois desaparecer. O Realm cria o Item localmente, sincroniza com o back-end e, em seguida, exclui o item porque não atende às regras de assinatura. Isso é chamado de gravação compensatória.
Você também notará que o documento que você criou inicialmente não está sincronizado, pois tem uma priority de null
. Se quiser que esse item seja sincronizado, você pode editar o documento na UI do Atlas e adicionar um valor para o campo de priority, ou você pode alterar sua inscrição para incluir documentos com valores nulos. Também daremos à inscrição o novo nome "getUserItemsWithHighOrNoPriority"
.
class RealmServices with ChangeNotifier { static const String queryAllName = "getAllItemsSubscription"; static const String queryMyItemsName = "getMyItemsSubscription"; static const String queryMyHighPriorityItemsName = "getMyHighPriorityItemsSubscription"; static const String queryMyHighOrNoPriorityItemsName = "getMyHighOrNoPriorityItemsSubscription"; bool showAll = false; bool offlineModeOn = false; bool isWaiting = false; late Realm realm; User? currentUser; App app; RealmServices(this.app) { if (app.currentUser != null || currentUser != app.currentUser) { currentUser ??= app.currentUser; realm = Realm(Configuration.flexibleSync(currentUser!, [Item.schema])); // Check if subscription previously exists on the realm final subscriptionDoesNotExists = realm.subscriptions.findByName(queryMyHighOrNoPriorityItemsName) == null; if (realm.subscriptions.isEmpty || subscriptionDoesNotExists) { updateSubscriptions(); } } } Future<void> updateSubscriptions() async { realm.subscriptions.update((mutableSubscriptions) { mutableSubscriptions.clear(); if (showAll) { mutableSubscriptions.add(realm.all<Item>(), name: queryAllName); } else { mutableSubscriptions.add( realm.query<Item>( r'owner_id == $0 AND priority IN {$1, $2, $3}', [currentUser?.id, PriorityLevel.high, PriorityLevel.severe, null], ), name: queryMyHighPriorityItemsName); } }); await realm.subscriptions.waitForSynchronization(); } // ... other methods }
Novamente, quando um erro StreamBuilder
ocorrer na primeira vez que você abrir o aplicativo com a nova inscrição, execute uma atualização instantânea para ver os dados esperados.
Conclusão
Adicionar uma propriedade a um objeto de Realm existente não é uma alteração significativa, e o modo de desenvolvimento garante que a alteração de esquema se reflita no lado do servidor.
O que vem a seguir?
Leia nossa documentação do Flutter SDK.
Encontre postagens de blog orientadas para desenvolvedores e tutoriais de integração no MongoDB Developer Hub.
Participe do Fórum da MongoDB Community para aprender com outros desenvolvedores e especialistas técnicos do MongoDB.
Explore exemplos de projetos de engenharia fornecidos por especialistas.
Observação
Compartilhar feedback
Como foi? Use o widget Rate this page no canto inferior direito da página para avaliar sua eficácia. Ou registre um problema no repositório GitHub se você teve algum problema.