Docs Menu
Docs Home
/ /
Atlas App Services

튜토리얼: Flutter용 Atlas Device Sync

이 페이지의 내용

  • 학습 목표
  • 전제 조건
  • 템플릿으로 시작하기
  • 템플릿 앱 설정하기
  • 앱 열기
  • 앱 구조 살펴보기
  • 앱 실행하기
  • 백엔드 확인하기
  • 애플리케이션 수정하기
  • 새 속성 추가
  • 모델에 새 속성 추가
  • 항목 생성 및 업데이트 시 우선 순위 설정하기
  • 실행 및 테스트
  • 구독 변경
  • 구독 업데이트
  • 실행 및 테스트
  • 결론
  • 다음 단계

예상 완료 시간: Flutter 사용 경험에 따라 30 분

Flutter용 Atlas Device SDK 를 사용하면 Dart 와 Flutter로 멀티 플랫폼 애플리케이션을 만들 수 있습니다. 이 튜토리얼은 flutter.todo.flex 라는 이름의 Flutter Flexible Sync Template 앱을 기반으로 하며, Todo 애플리케이션 을 생성하는 방법을 보여줍니다. 이 애플리케이션 을 통해 사용자는 다음을 수행할 수 있습니다.

  • 이메일을 새 사용자 계정으로 등록합니다.

  • 이메일과 비밀번호를 사용하여 계정에 로그인하고 나중에 로그아웃합니다.

  • 작업 항목 보기, 만들기, 수정 및 삭제하기.

  • 사용자가 소유자가 아닌 경우에도 모든 작업을 볼 수 있습니다.

템플릿 앱은 장치가 '오프라인 모드'에 있는 것을 시뮬레이션하는 토글도 제공합니다. 이 토글을 사용하면 인터넷에 연결되어 있지 않은 사용자를 에뮬레이션하여 Device Sync 기능을 빠르게 테스트할 수 있습니다. 그러나 프로덕션 애플리케이션에서는 이 토글을 제거할 가능성이 높습니다.

이 튜토리얼은 템플릿 앱에 추가됩니다. 기존 Item 모델에 새 priority 필드를 추가하고 Flexible Sync 구독을 업데이트하여 우선 순위 범위 내의 항목만 표시합니다.

이 튜토리얼에서는 필요에 맞게 템플릿 앱을 조정하는 방법을 보여줍니다. 템플릿 앱의 현재 구조를 고려할 때 반드시 이렇게 변경할 필요는 없습니다.

이 튜토리얼에서는 다음을 알아봅니다.

  • 호환성이 손상되지 않는 변경을 적용하여 Realm 객체 모델을 업데이트합니다.

  • Device Sync 구독 업데이트

  • 동기화되는 데이터를 변경하려면 서버의 Device Sync 구성에 쿼리 가능한 필드를 추가하십시오.

안내된 튜토리얼을 따르지 않고 직접 애플리케이션 을 시작하는 것을 선호하는 경우 Flutter 빠른 시작 을 확인하세요. 여기에는 복사 가능한 코드 예제와 Flutter SDK 애플리케이션 을 설정하다 하는 데 필요한 필수 정보가 포함되어 있습니다.

  • Android 에뮬레이터, iOS 시뮬레이터 및/또는 실제 기기에 Flutter 앱을 배포한 경험이 있어야 합니다.

  • 이 튜토리얼은 템플릿 앱으로 시작합니다. 템플릿 앱을 만들려면 Atlas 계정, API 키, Atlas App Services CLI가 필요합니다.

    • Atlas 계정 만들기에 관한 자세한 사항은 Atlas 시작하기 문서에서 확인할 수 있습니다. 이 튜토리얼을 사용하려면 프리 티어 클러스터가 있는 Atlas 계정이 필요합니다.

    • 로그인하려는 MongoDB Cloud 계정에 대한 Atlas API 키도 필요합니다. App Services CLI를 사용해 템플릿 앱을 만들려면 프로젝트 소유자여야 합니다.

    • App Services CLI 설치에 대해 자세히 알아보려면 App Services CLI 설치를 참조하세요. 설치 후 Atlas 프로젝트의 API 키를 사용하여 login 명령을 실행합니다.

참고

지원 플랫폼

다음 플랫폼에서 튜토리얼 앱을 빌드할 수 있습니다:

  • iOS

  • Android

  • macOS

  • Windows

  • Linux

Flutter SDK는 웹 애플리케이션 빌드를 지원하지 않습니다.

이 튜토리얼은 flutter.todo.flex(이)라는 Flutter Flexible Sync Template 앱을 기반으로 합니다. 기본 앱으로 시작하여 그 위에 새로운 기능을 빌드합니다.

템플릿 앱에 관한 자세한 사항은 템플릿 앱을 참조하십시오.

아직 Atlas 계정이 없는 경우 등록하여 템플릿 앱을 배포합니다.

App Services App 만들기 가이드 에 설명된 절차에 따라 Create App from Template 을 선택합니다. Real-time Sync 템플릿을 선택합니다. 이렇게 하면 Device Sync 템플릿 앱 클라이언트 중 하나와 함께 사용하도록 사전 구성된 App Services App 이 생성됩니다.

템플릿 앱을 만들면 UI에 Get the Front-end Code for your Template이라는 레이블이 붙은 모달이 표시됩니다. 이 모달은 템플릿 앱 클라이언트 코드를 .zip 파일로 다운로드하거나 App Services CLI를 사용하여 클라이언트를 가져오는 방법에 대한 지침을 제공합니다.

.zip 또는 App Services CLI 메서드를 선택한 후 화면의 안내에 따라 클라이언트 코드를 가져옵니다. 이 튜토리얼에서는 Dart (Flutter) 클라이언트 코드를 선택합니다.

다운로드한 앱의 압축을 풀면 Flutter 앱이 표시됩니다.

참고

기본값 Windows ZIP 유틸리티에 .zip이 표시될 수 있습니다. 파일 을 빈 상태로 표시합니다. 이 문제가 발생하면 사용 가능한 타사 zip 프로그램 중 하나를 사용하세요.

appservices apps create 명령은 백엔드를 설정하고 이 튜토리얼의 기반으로 사용할 Flutter 템플릿 앱을 만듭니다.

터미널 창에서 다음 명령을 실행하여 US-VA 리전에 배포되고 환경이 'development(production 또는 QA가 아닌)'로 설정된 'MyTutorialApp'이라는 앱을 만듭니다.

appservices app create \
--name MyTutorialApp \
--template flutter.todo.flex \
--deployment-model global \
--environment development

이 명령은 --name 플래그의 값과 동일한 이름으로 현재 경로에 새 디렉토리를 작성합니다.

클라이언트 코드가 포함된 리포지토리 를 Github 포크하고 복제할 수 있습니다.Device Sync Flutter 클라이언트 코드는 https://github.com/mongodb/ Template-app-dart-flutter-todo에서 확인할 수 있습니다.

이 프로세스를 사용하여 클라이언트 코드를 가져오는 경우 클라이언트와 함께 사용할 템플릿 앱을 만들어야 합니다. 템플릿 앱 만들기의 지침에 따라 Atlas App Services UI, App Services CLI, 또는 Admin API를 사용하여 Device Sync 백엔드 템플릿 앱을 만듭니다.

저장소를 복제하고 README의 지침에 따라 백엔드 앱 ID를 Flutter 앱에 추가합니다.

1

코드 편집기로 Flutter 앱을 엽니다.

클라이언트를 .zip 파일로 다운로드했거나 클라이언트 GitHub 리포지토리를 복제한 경우 클라이언트의 적절한 위치에 App Services App ID를 수동으로 삽입해야 합니다. 앱 ID를 삽입할 위치를 알아보려면 클라이언트 README.mdConfiguration 지침을 따르세요.

2

코드 편집기에서 잠시 시간을 내어 프로젝트 가 어떻게 구성되는지 살펴보세요. 이는 특정 용도에 맞게 수정된 표준 멀티 플랫폼 Flutter 애플리케이션 입니다. 구체적으로 다음 파일에는 Flutter SDK 의 중요한 용도가 포함되어 있습니다.

file
목적
lib/main.dart
앱의 진입 지점입니다. 라우팅 및 상태 관리가 포함되어 있습니다.
lib/realm/schemas.dart
Realm 데이터베이스 스키마를 정의합니다.
lib/realm/schemas.realm.dart
생성된 Realm 객체 클래스입니다.
lib/realm/app_services.dart
Atlas App Services와의 상호 작용을 처리합니다.
lib/realm/realm_services.dart
Realm 데이터베이스 및 Atlas Device Sync와의 상호 작용을 처리합니다.
lib/components/
Flutter 위젯이 포함된 앱의 컴포넌트입니다.
lib/screens/
앱의 페이지.
3

코드를 변경하지 않고도 Android 에뮬레이터, iOS 시뮬레이터, 실제 모바일 기기 또는 데스크톱 에뮬레이터에서 앱을 실행할 수 있어야 합니다.

먼저 터미널에서 다음을 실행하여 모든 종속성을 설치합니다:

flutter pub get

그런 다음 장치에 연결하고 Flutter 애플리케이션을 실행합니다.

앱이 실행되면 새 사용자 계정을 등록한 다음 주요 할 일(To-do) 목록에 새 항목을 추가하세요.

개발 도구를 사용하여 Flutter 앱 을 실행 하는 방법에 대한 자세한 내용은 Flutter 테스트 드라이브 문서를 참조하세요.

4

MongoDB Atlas 에 로그인합니다. Data Services 탭 에서 Browse Collections 를 클릭합니다. 데이터베이스 목록에서 todo 데이터베이스 를 찾아 확장한 다음 Item 컬렉션 을 찾아 확장합니다. 이 컬렉션 에서 만든 문서 가 표시되어야 합니다.

1

이제 모든 것이 예상대로 작동하는지 확인했으므로 변경 사항을 추가할 수 있습니다. 이 튜토리얼에서는 우선순위에 따라 항목을 필터링할 수 있도록 각 항목에 "우선순위" 속성을 추가하기로 결정했습니다.

이렇게 하려면 다음 단계를 따라 진행합니다.

  1. flutter.todo.flex 프로젝트에서 lib/realm/schemas.dart 파일을 엽니다.

  2. _Item 클래스에 다음 속성을 추가합니다.

    lib/realm/schemas.dart
    late int? priority;
  3. Item 0} Realm 객체 클래스를 다시 생성합니다:

    dart run realm generate
2
  1. lib/realm/realm_services.dart에서 priority를 설정하고 업데이트하는 로직을 추가합니다. 또한 RealmServices 클래스 아래에 PriorityLevel 추상 클래스를 추가하여 사용 가능한 값을 제한합니다.

    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();
    }
    @override
    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;
    }
  2. 항목의 우선순위를 설정하는 위젯을 포함할 새 파일을 추가합니다. 파일 lib/components/select_priority.dart을 만듭니다.

    lib/components/select_priority.dart
    import '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);
    @override
    State<SelectPriority> createState() => _SelectPriorityState();
    }
    class _SelectPriorityState extends State<SelectPriority> {
    @override
    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")),
    ],
    ),
    ],
    ),
    );
    }
    }
  3. SelectPriority 위젯을 CreateItemModifyItem위젯에 더합니다. 또한 우선순위 설정을 처리하기 위해 몇 가지 로직을 추가해야 합니다. 추가해야 하는 코드는 아래와 같습니다.

    추가하는 파일의 일부 섹션은 변경된 코드의 관련 섹션에 초점을 맞추기 위해 아래 코드 예시에서 주석으로 대체되었습니다.

    CreateItemForm 위젯을 lib/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);
    @override
    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
    @override
    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);
    }
    }
    }

    ModifyItemForm 위젯을 lib/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);
    @override
    _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);
    @override
    void initState() {
    _summaryController = TextEditingController(text: item.summary);
    _isCompleteController = ValueNotifier<bool>(item.isComplete)
    ..addListener(() => setState(() {}));
    _priority = widget.item.priority;
    super.initState();
    }
    @override
    void dispose() {
    _summaryController.dispose();
    _isCompleteController.dispose();
    super.dispose();
    }
    @override
    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);
    }
    }
  4. 이제 lib/components/todo_item.dartItemCard 위젯에 우선 순위에 대한 시각적 표시기를 추가합니다. 항목의 우선 순위를 시각적으로 표시하는 새 위젯 _PriorityIndicator를 만듭니다.

    방금 만든 _PriorityIndicator 위젯을 TodoItem 위젯에 추가합니다.

    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);
    @override
    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();
    }
    }
    @override
    Widget build(BuildContext context) {
    return getIconForPriority(priority);
    }
    }
3

애플리케이션을 다시 실행하기 전에 빠른 새로고침(hot restart)을 수행합니다. 이렇게 하면 동기화 세션이 새 스키마로 다시 시작되고 동기화 오류가 방지됩니다.

그런 다음 이 튜토리얼의 앞부분에서 생성한 계정을 사용하여 로그인합니다. 이전에 생성한 하나의 항목이 표시됩니다. 새 항목을 추가하면 이제 우선 순위를 설정할 수 있는 것을 볼 수 있습니다. 우선 순위를 High로 선택하고 항목을 저장합니다.

이제 브라우저에서 Atlas 데이터 페이지로 다시 전환하고 Item 컬렉션을 새로 고침 하십시오. 그러면 priority 필드가 추가되고 1로 설정된 새 항목이 표시됩니다. 또한 다음 스크린샷과 같이 기존 항목에도 priority 필드가 있으며, 이 필드가 null로 설정되어 있음을 알 수 있습니다.

컬렉션에 있는 두 가지 항목
클릭하여 확대

참고

동기화가 해제되지 않은 이유는 무엇인가요?

Realm 객체에 속성을 추가하는 것은 호환성이 손상되는 변경 사항이 아니므로 클라이언트를 재설정할 필요가 없습니다. 템플릿 앱에는 개발 모드가 활성화되어 있으므로 클라이언트 Realm 객체에 대한 변경 사항이 서버 측 스키마에 반영됩니다. 자세한 내용은 개발 모드데이터 모델 업데이트를 참조하세요.

이제 우선순위 필드를 추가했으므로 우선순위가 높음 또는 심각으로 표시된 항목만 동기화하도록 장치 동기화 구독을 업데이트하려고 합니다.

1

lib/realm/realm_services.dart 파일에서는 사용자의 장치 및 계정과 동기화할 문서를 정의하는 Flexible Sync 구독을 정의합니다. 현재 owner 속성이 인증된 사용자와 일치하는 모든 문서를 동기화하고 있습니다.

현재 구독입니다:

lib/realm/realm_services.dart
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();
}

이제 높음 및 심각 수준의 우선순위 항목만 동기화하도록 구독을 변경하겠습니다. 기억하시겠지만 우선 순위 필드는 int 유형입니다. 여기서 가장 높은 우선 순위('위험')의 값은 0이고 가장 낮은 우선 순위('낮음')의 값은 3입니다.

int와 priority 속성을 직접 비교할 수 있습니다. 이를 위해 우선 순위가 PriorityLevel.high (또는 1) 보다 작거나 같은 항목을 포함하도록 구독 쿼리를 리팩터링할 것입니다. 또한 구독에 "getMyHighPriorityItemsSubscription"이라는 새 이름을 지정합니다.

구독을 업데이트하여 이전 구독을 삭제하고 우선 순위를 사용하는 새 구독을 추가합니다.

lib/realm/realm_services.dart
// ... 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
}
2

애플리케이션을 다시 실행합니다. 이 튜토리얼의 앞부분에서 생성한 계정을 사용하여 로그인합니다.

Realm이 문서 컬렉션을 다시 동기화하는 초기 시점 이후에는 다음과 같은 오류 메시지가 표시될 수 있습니다 .

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

이 오류는 구독이 업데이트될 때 StreamBuilder 위젯에서 발생할 수 있습니다. 프로덕션 앱에서 오류 처리를 추가할 수 있습니다. 이 튜토리얼의 경우 핫 새로고침을 수행하면 오류가 사라집니다.

이제 새로 생성한 우선 순위가 높은 항목이 표시됩니다.

개발자 모드가 활성화된 상태에서 구독 변경하기

이 튜토리얼에서는 구독을 변경하고 처음으로 우선 순위 필드에 대한 쿼리를 하면 해당 필드가 Device Sync Collection Queryable Fields에 자동으로 추가됩니다. 이는 템플릿 앱에 개발 모드가 기본적으로 활성화되어 있기 때문입니다. 개발 모드가 활성화되지 않은 경우 클라이언트 사이드 동기화 쿼리에서 필드를 사용하려면 쿼리 가능 필드로 수동으로 추가해야 합니다.

자세한 내용은 쿼리 가능 필드를 참조하세요.

기능을 추가로 테스트하려면 다양한 우선 순위의 항목을 생성할 수 있습니다. 우선 순위가 낮은 새 항목이 항목 목록에 잠시 나타났다가 사라지는 것을 볼 수 있습니다. Realm은 항목을 로컬에서 생성하고 백엔드와 동기화한 다음 구독 규칙을 충족하지 않기 때문에 항목을 삭제합니다. 이를 보상 쓰기라고 합니다.

또한 처음에 생성한 문서는 우선순위가 null 이므로 동기화되지 않습니다. 이 항목을 동기화하려면 Atlas UI에서 문서를 편집하고 우선 순위 필드에 값을 추가하거나, null 값이 있는 문서를 포함하도록 구독을 변경할 수 있습니다. 또한 구독에 "getUserItemsWithHighOrNoPriority"이라는 새 이름을 지정합니다.

lib/realm/realm_services.dart
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
}

다시 말하지만, 새 구독으로 앱을 처음 열 때 StreamBuilder 오류가 발생하면 빠른 새로고침(hot refresh)을 수행하여 예상 데이터를 확인합니다.

기존 Realm 객체에 속성을 추가하는 것은 호환성이 손상되지 않는 변경이며, 개발 모드는 스키마 변경이 서버 측에 반영되도록 합니다.

참고

피드백 공유

어떻게 되었나요? 페이지 오른쪽 하단의 Rate this page 위젯을 사용하여 효과를 평가하세요. 문제가 있었다면 GitHub 리포지토리에 이슈를 제출하세요.

다음

Atlas Application Services란 무엇인가요?