Connecting MongoDB to a Flutter App Using Realm : A Step-by-Step Guide

MongoDB is a popular NoSQL database known for its flexibility and scalability. Integrating MongoDB with a Flutter app can be a powerful combination. In this blog, we’ll explore how to connect MongoDB to a Flutter app using the realmpackage.

Prerequisites

Before we begin, make sure you have the following prerequisites:

  1. Flutter Installed: Ensure that you have Flutter and Dart set up on your development machine. You can download Flutter from here.

  2. Server: MongoDB should be installed and running. You can download and install MongoDB from here.

  3. MongoDB Database: Create a MongoDB database where your app data will be stored.

Setting Up the Flutter Project

Let’s create a new Flutter project or use an existing one.

  1. Add Dependencies:

In your pubspec.yaml file, add the realm package:

dependencies:
  flutter:
    sdk: flutter
  realm: ^latest_version

Replace latest_version with the actual version of realm.

  1. Run flutter pub get to fetch and install the dependencies.

Step 1: Import the Package

In your Flutter project, import the realm package in the Dart file where you intend to work with MongoDB.

import 'package:realm/realm.dart';

Step 2: Establish a Connection

To connent your flutter app to mongodb database you need to make project in mongodb cloud and create one project

If you don’t know how to create realm project in mongo db then follow below instruction.

  1. Create account in mongodb cloud

  2. Create a project

Click on your rightside of Altas logo there popup will open scroll down then you will see create new mongo db project

After creating mongo db project you will see this option

This will shown after click on create database option in your mongodb project

After creating database click on database and inside database click on collection then create a new own collection

Click on App services then you will get this option

Let’s start write a code how you connect app to mongo db

In this tutorial we will use provider for state management

  1. Create config.json to add your configuration
{
    "appId": "write app id",
    "appUrl": "https://realm.mongodb.com/groups/{delete this url and add your realm app url}",
    "baseUrl": "https://realm.mongodb.com",
    "clientApiBaseUrl": "https://ap-south-1.aws.realm.mongodb.com",
    "dataApiBaseUrl": "https://ap-south-1.aws.data.mongodb-api.com",
    "dataExplorerLink": "https://cloud.mongodb.com/links/  {delete this url and add your cloud app url} ",
    "dataSourceName": "mongodb-atlas"
}

Create a schema.dart add code in below format

import 'package:realm/realm.dart';

part 'schemas.g.dart';

@RealmModel()
@MapTo('listing')
class _Listing {
  @PrimaryKey()
  @MapTo('_id')
  late ObjectId id;

  int? bathroomCount;

  late String category;

  late String country;

  DateTime? createdAt;

  String? description;

  late List<_ListingFacilities> facilities;

  int? guestCount;

  String? hotelspecification;

  late List<String> imageSrc;

  late List<double> locationValue;

  late String placename;

  String? placenameCode;

  late int price;

  double? rating;

  int? roomCount;

  late String title;

  ObjectId? userId;
}

@RealmModel(ObjectType.embeddedObject)
@MapTo('listing_facilities')
class _ListingFacilities {
  String? fav;

  String? icon;
}

@RealmModel()
@MapTo('place')
class _Place {
  @PrimaryKey()
  @MapTo('_id')
  late ObjectId id;
  late String category;
  late String country;
  DateTime? createdAt;
  String? description;
  late List<_PlaceFacilities> facilities;
  late List<String> imageSrc;
  late String location;
  late List<double> locationValue;
  late int price;
  String? rating;
  late String title;
}

@RealmModel(ObjectType.embeddedObject)
@MapTo('place_facilities')
class _PlaceFacilities {
  String? fac;

  String? icon;
}

@RealmModel()
@MapTo('userdata')
class _Userdata {
  @PrimaryKey()
  @MapTo('_id')
  ObjectId? id;

  @MapTo('owner_id')
  late String ownerId;

  String? country;

  DateTime? createdAt;

  late String email;

  bool? emailVerified;

  String? name;

  late String password;

  String? phonenumber;

  DateTime? updatedAt;

  // List<_Reservations> reservations = [];
}

@RealmModel()
@MapTo('allreservation')
class _AllReservations {
  @PrimaryKey()
  @MapTo('_id')
  late ObjectId id;
  DateTime? createdAt;
  late DateTime enddate;
  late ObjectId listingId;
  late DateTime startDate;
  late int totalprice;
  late ObjectId userId;
}
dart run realm generate --clean

Run this command after creating schema.dart

Now you will see new file called schema.g.dart

Create app services and realm services

Write below code to create app services with login and signup

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:realm/realm.dart';
import 'package:travel_app/realm/realm_services.dart';

class AppServices with ChangeNotifier {
  String id;
  Uri baseUrl;
  App app;
  User? currentUser;
  AppServices(this.id, this.baseUrl)
      : app = App(AppConfiguration(id, baseUrl: baseUrl));

  Future<Map<String, dynamic>> logInUserEmailPassword(
      String email, String password, BuildContext context) async {
    try {
      await currentUser?.logOut();
      User loggedInUser =
          await app.logIn(Credentials.emailPassword(email, password));
      if (!context.mounted) {
        return {"success": false, "message": "Some error occurred!"};
      }
      final realmServices = Provider.of<RealmServices>(context, listen: false);
      currentUser = loggedInUser;
      realmServices.currentUser = loggedInUser;
      notifyListeners();
      return {"success": true, "message": "Login successfully!"};
    } catch (e) {
      return {"success": false, "message": "Some error occurred!"};
    }
  }

  Future<Map<String, dynamic>> registerUserEmailPassword(
      String email, String password, String name, BuildContext context) async {
    try {
      await currentUser?.logOut();
      EmailPasswordAuthProvider authProvider = EmailPasswordAuthProvider(app);
      await authProvider.registerUser(email, password);
      User loggedInUser =
          await app.logIn(Credentials.emailPassword(email, password));
      if (!context.mounted) {
        return {"success": false, "message": "Some error occurred!"};
      }
      final realmServices = Provider.of<RealmServices>(context, listen: false);
      await realmServices.createUser(email, password, name, loggedInUser.id);
      loggedInUser.logOut();
      notifyListeners();
      return {"success": true, "message": "User created successfully!"};
    } catch (e) {
      // if (e.runtimeType == RealmInvalidCredentialsException) {}else{}
      return {"success": false, "message": "Some error occurred!"};
    }
  }

  Future<void> logOut() async {
    await currentUser?.logOut();
    currentUser = null;
  }
}

Create file called realm_service.dart

import 'package:travel_app/realm/schemas.dart';
import 'package:realm/realm.dart';
import 'package:flutter/material.dart';

class RealmServices with ChangeNotifier {
  static const String queryAllName = "getAllItemsSubscription";
  static const String queryMyItemsName = "getMyItemsSubscription";

  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!, [
        Userdata.schema,
        Place.schema,
        Listing.schema,
        ListingFacilities.schema,
        PlaceFacilities.schema,
        AllReservations.schema
      ]));
      if (realm.subscriptions.isEmpty) {
        updateSubscriptions();
        notifyListeners();
      }
    }
  }

  Future<void> updateSubscriptions() async {
    realm.subscriptions.update((mutableSubscriptions) {
      mutableSubscriptions.clear();
      mutableSubscriptions.add(realm.all<Userdata>());
      mutableSubscriptions.add(realm.all<Place>());
      mutableSubscriptions.add(realm.all<Listing>());
      mutableSubscriptions.add(realm.all<AllReservations>());
    });
    await realm.subscriptions.waitForSynchronization();
    notifyListeners();
  }

  Future<void> sessionSwitch() async {
    offlineModeOn = !offlineModeOn;
    if (offlineModeOn) {
      realm.syncSession.pause();
    } else {
      try {
        isWaiting = true;
        notifyListeners();
        realm.syncSession.resume();
        await updateSubscriptions();
      } finally {
        isWaiting = false;
      }
    }
    notifyListeners();
  }

  Future<void> switchSubscription(bool value) async {
    if (!offlineModeOn) {
      try {
        isWaiting = true;
        notifyListeners();
        await updateSubscriptions();
      } finally {
        isWaiting = false;
      }
    }
    notifyListeners();
  }


  Future<void> createUser(
      String email, String password, String name, String ownerId) async {
    try {
      final newItem = Userdata(
        ObjectId(),
        ownerId,
        email,
        password,
        name: name,
        createdAt: DateTime.now(),
        updatedAt: DateTime.now(),
        emailVerified: false,
      );
      realm.write<Userdata>(() => realm.add<Userdata>(newItem));
      print("Hello");
    } catch (e) {
      print(e);
    } finally {
      notifyListeners();
    }
  }


  Future<void> close() async {
    if (currentUser != null) {
      currentUser = null;
    }
    realm.close();
  }
}

add below code in main.dart file

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:travel_app/screens/homescreen.dart';
import 'package:travel_app/screens/onboardingscreen.dart';
import 'package:travel_app/screens/onboardingslider.dart';
import 'package:travel_app/screens/splashscreen.dart';
import 'package:travel_app/themes/dark_theme.dart';
import 'package:travel_app/themes/light_theme.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:travel_app/realm/realm_services.dart';
import 'package:travel_app/realm/app_services.dart';
import 'dart:convert';
import 'package:realm/realm.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  Config realmConfig = await Config.getConfig('assets/config/atlasConfig.json');
  final appConfig = AppConfiguration(realmConfig.appId);
  final app = App(appConfig);
  if (app.currentUser == null) {
    try {
      final anonCredentials = Credentials.anonymous();
      await app.logIn(anonCredentials);
    } catch (e) {
      print(e);
    }
  }
  runApp(MultiProvider(
    providers: [
      ChangeNotifierProvider<Config>(create: (_) => realmConfig),
      ChangeNotifierProvider<AppServices>(
          create: (_) => AppServices(realmConfig.appId, realmConfig.baseUrl)),
      ChangeNotifierProxyProvider<AppServices, RealmServices?>(
          // RealmServices can only be initialized only if the user is logged in.
          create: (context) => null,
          update: (BuildContext context, AppServices appServices,
              RealmServices? realmServices) {
            return RealmServices(appServices.app);
          }),
    ],
    builder: (context, child) {
      return const MyApp();
    },
  ));
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final realmServices = Provider.of<RealmServices>(context);
    return MaterialApp(
      theme: lightTheme,
      themeMode: ThemeMode.system,
      darkTheme: lightTheme,
      debugShowCheckedModeBanner: false,
      home: realmServices.currentUser != null &&
              realmServices.currentUser!.provider != AuthProviderType.anonymous
          ? const HomeScreen()
          : const SplashScreen(),
      // home: const LoginScreen(),
      // home: const OnboardingScreen(),
    );
  }
}

class Config extends ChangeNotifier {
  late String appId;
  late String atlasUrl;
  late Uri baseUrl;

  Config._create(dynamic realmConfig) {
    appId = realmConfig['appId'];
    atlasUrl = realmConfig['dataExplorerLink'];
    baseUrl = Uri.parse(realmConfig['baseUrl']);
  }

  static Future<Config> getConfig(String jsonConfigPath) async {
    dynamic realmConfig =
        json.decode(await rootBundle.loadString(jsonConfigPath));
    var config = Config._create(realmConfig);

    return config;
  }
}

If you have any queries then check this repository

https://github.com/Kailash8799/Travelapp_Flutter