Data Persistence Techniques


Data Persistence Techniques Interview with follow-up questions

1. Can you explain some of the techniques for data persistence in Flutter?

The common local-persistence techniques in Flutter, chosen by data shape:

  1. shared_preferences — key-value for small, simple data (settings, flags, last-seen IDs). Prefer the newer SharedPreferencesAsync / SharedPreferencesWithCache APIs over the legacy singleton. Unencrypted, so not for secrets.

  2. SQLite via sqflite — a relational SQL database for structured, queryable data. Raw and async, but you write SQL by hand and map rows yourself (no type safety).

  3. Drift — a type-safe, reactive layer over SQLite; a frequent default in 2026. Compile-checked queries and Streams that push updates to the UI when rows change.

  4. NoSQL object storesHive CE (lightweight key-value, community-maintained), Isar v4 (community), and ObjectBox (high-performance) for persisting Dart objects without SQL.

  5. Secure storageflutter_secure_storage for tokens/secrets, backed by Keychain/Keystore.

  6. Filespath_provider + dart:io for blobs, images, or custom file formats.

Interview follow-ups:

  • Encryption: shared_preferences is plaintext — use secure storage for anything sensitive.
  • sqflite vs Drift: raw SQL vs type-safe reactive SQL — name the trade-off.
  • Firebase/Supabase add cloud + real-time sync, but pair them with a local cache for offline-first behavior.
  • All of this is async I/O — keep it off the build path, and plan for schema migrations as models evolve.
↑ Back to top

Follow-up 1

What are the advantages and disadvantages of each technique?

The advantages and disadvantages of each technique for data persistence in Flutter are as follows:

  1. Shared Preferences:

    • Advantages: Easy to use, lightweight, and suitable for storing small amounts of data.
    • Disadvantages: Limited to key-value pairs, not suitable for complex data structures or large amounts of data.
  2. SQLite:

    • Advantages: Provides a structured and efficient way to store and query data, suitable for larger amounts of structured data.
    • Disadvantages: Requires knowledge of SQL, can be more complex to set up and maintain compared to other techniques.
  3. File System:

    • Advantages: Can store any type of file, suitable for larger amounts of unstructured data.
    • Disadvantages: Requires manual management of file paths and permissions, can be slower for complex data operations.
  4. Firebase:

    • Advantages: Real-time synchronization, scalable, and easy to set up.
    • Disadvantages: Requires internet connectivity, may incur additional costs for high usage.

Follow-up 2

Can you provide a practical example where you would use each technique?

Sure! Here are some practical examples where you would use each technique for data persistence in Flutter:

  1. Shared Preferences: You can use shared preferences to store user preferences, such as theme settings, language preferences, or user authentication tokens.

  2. SQLite: If you have a Flutter app that requires a local database to store and query structured data, such as a to-do list app with multiple tasks and categories, you can use SQLite for data persistence.

  3. File System: If your app needs to store and retrieve large files, such as images or documents, you can use the file system for data persistence. For example, a photo gallery app that allows users to save and view their photos.

  4. Firebase: If you want to build a real-time collaborative app, such as a chat app or a shared to-do list app, you can use Firebase for data persistence. Firebase provides real-time synchronization, allowing multiple users to see updates in real-time.

Follow-up 3

How would you choose the right technique for a particular application?

When choosing the right technique for data persistence in a particular application, consider the following factors:

  1. Data Size and Complexity: If your app requires storing large amounts of structured data, SQLite may be a suitable choice. For smaller amounts of data or simple key-value pairs, shared preferences may be sufficient.

  2. Performance Requirements: Consider the performance requirements of your app. If you need fast read and write operations, SQLite or Firebase may be better choices compared to the file system.

  3. Offline Support: If your app needs to work offline or have offline access to data, SQLite or the file system may be more suitable, as they allow local storage and retrieval of data.

  4. Backend Integration: If your app requires real-time synchronization or needs to interact with a cloud-based backend, Firebase may be a good choice due to its seamless integration with Flutter.

  5. Development Experience: Consider your familiarity with each technique and the development effort required to implement and maintain it.

Follow-up 4

What are the security considerations for each technique?

The security considerations for each technique for data persistence in Flutter are as follows:

  1. Shared Preferences: Shared preferences are stored as plain text XML files, so sensitive data should not be stored using this technique. If you need to store sensitive data, consider encrypting it before storing it in shared preferences.

  2. SQLite: SQLite databases can be encrypted using third-party libraries or plugins. It is recommended to encrypt sensitive data stored in SQLite databases to protect it from unauthorized access.

  3. File System: When storing sensitive data in files, consider encrypting the data and ensuring proper file permissions to prevent unauthorized access. Be cautious when storing sensitive data on external storage, as it may be accessible to other apps or users.

  4. Firebase: Firebase provides built-in security rules and authentication mechanisms to protect data. It is important to properly configure security rules and use secure authentication methods to ensure the security of data stored in Firebase.

Follow-up 5

How does data persistence in Flutter compare to other frameworks?

Data persistence in Flutter is similar to other frameworks in terms of the techniques available, such as shared preferences, SQLite, file system, and cloud-based solutions. However, Flutter provides a unified and consistent API for data persistence, making it easier to work with compared to frameworks that may have different APIs for each technique.

Additionally, Flutter's hot reload feature allows for faster development and testing of data persistence logic compared to some other frameworks.

However, it's worth noting that the choice of data persistence technique may vary depending on the specific framework and its ecosystem. Some frameworks may have additional or different techniques for data persistence, and the performance and features of each technique may vary between frameworks.

2. How do you use SQLite in Flutter for data persistence?

You use SQLite in Flutter through the sqflite package, which exposes an async API over the native SQLite engine on iOS/Android. (For desktop/web, add sqflite_common_ffi / sqflite_common_ffi_web.)

  1. Add the dependency:
dependencies:
  sqflite: ^2.4.0
  path: ^1.9.0
  1. Open the database and create tables in onCreate:
final db = await openDatabase(
  join(await getDatabasesPath(), 'app.db'),
  version: 1,
  onCreate: (db, version) => db.execute(
    'CREATE TABLE todos(id INTEGER PRIMARY KEY, title TEXT, done INTEGER)',
  ),
);
  1. Run typed CRUD with parameterized queries:
await db.insert('todos', {'title': 'Buy milk', 'done': 0});
final rows = await db.query('todos', where: 'done = ?', whereArgs: [0]);
await db.update('todos', {'done': 1}, where: 'id = ?', whereArgs: [1]);
await db.delete('todos', where: 'id = ?', whereArgs: [1]);

Interview follow-ups:

  • Always use ? placeholders / whereArgs — string-concatenating values risks SQL injection.
  • Migrations: bump version and implement onUpgrade to ALTER tables; forgetting this corrupts upgrades.
  • No type safety: query returns List> you map by hand — which is exactly why many teams reach for Drift (type-safe, reactive queries over the same SQLite).
  • Wrap multi-write operations in a transaction for atomicity, and keep heavy work off the UI isolate.
↑ Back to top

Follow-up 1

Can you explain how to set up SQLite in a Flutter project?

To set up SQLite in a Flutter project, follow these steps:

  1. Add the sqflite package to your pubspec.yaml file:
dependencies:
  sqflite: ^2.0.0
  1. Import the sqflite package in your Dart file:
import 'package:sqflite/sqflite.dart';
  1. Open a connection to the SQLite database:
Database database = await openDatabase(
  'path_to_database.db',
  version: 1,
  onCreate: (db, version) {
    // Create tables and initialize the database
  },
);
  1. Perform CRUD operations on the database using the provided APIs.

Follow-up 2

What are the advantages of using SQLite?

There are several advantages of using SQLite for data persistence in Flutter:

  1. Lightweight: SQLite is a lightweight database engine that doesn't require a separate server process.

  2. Fast: SQLite is known for its fast performance, especially for read-heavy workloads.

  3. Cross-platform: SQLite is a cross-platform database engine that works on various operating systems, including Android, iOS, and desktop platforms.

  4. ACID-compliant: SQLite supports ACID (Atomicity, Consistency, Isolation, Durability) properties, ensuring data integrity and reliability.

  5. Wide adoption: SQLite is widely adopted and has a large community, which means there are plenty of resources and libraries available for working with SQLite in Flutter.

Follow-up 3

What are the limitations of SQLite?

While SQLite is a powerful database engine, it also has some limitations:

  1. Single-user access: SQLite is designed for single-user access and doesn't support concurrent write operations. If multiple threads or processes try to write to the database simultaneously, it can lead to data corruption.

  2. Limited data types: SQLite has a limited set of data types compared to other databases. For example, it doesn't have a dedicated boolean data type.

  3. No network access: SQLite doesn't have built-in support for network access. If you need to synchronize data between multiple devices or access a remote database, you'll need to implement custom solutions.

  4. No server-side processing: SQLite is an embedded database engine, which means it runs within the same process as the application. It doesn't support server-side processing or distributed computing.

Follow-up 4

How would you handle complex queries in SQLite?

To handle complex queries in SQLite, you can use the rawQuery method provided by the sqflite package. This method allows you to execute raw SQL queries and retrieve the results as a list of maps. Here's an example:

List> result = await database.rawQuery('SELECT * FROM table WHERE condition');

Follow-up 5

Can you explain how to use SQLite in conjunction with a state management solution?

When using SQLite in conjunction with a state management solution in Flutter, you can follow these steps:

  1. Define a data model class that represents the data stored in the SQLite database.

  2. Use a state management solution like Provider or Riverpod to manage the state of your Flutter application.

  3. Create a repository class that handles the CRUD operations on the SQLite database. This class should expose methods for retrieving, inserting, updating, and deleting data.

  4. Use the state management solution to inject an instance of the repository class into your Flutter widgets.

  5. In your Flutter widgets, use the repository class to interact with the SQLite database and update the state of your application.

By using a state management solution, you can separate the concerns of data persistence and UI rendering, making your code more modular and testable.

3. What is shared preferences and how is it used in Flutter?

shared_preferences is a plugin that stores small key-value pairs on the device (backed by NSUserDefaults on iOS and SharedPreferences on Android), surviving app restarts. It's the go-to for simple data — theme choice, onboarding-seen flag, last selected tab — supporting bool, int, double, String, and List.

In 2026 prefer the newer async APIs over the legacy singleton:

// SharedPreferencesAsync — no cached singleton, always hits the platform store
final prefs = SharedPreferencesAsync();
await prefs.setBool('darkMode', true);
final dark = await prefs.getBool('darkMode') ?? false;

// SharedPreferencesWithCache — async read of a known key set into memory
final cache = await SharedPreferencesWithCache.create(
  cacheOptions: const SharedPreferencesWithCacheOptions(),
);
final dark2 = cache.getBool('darkMode') ?? false; // sync read from cache

Interview follow-ups:

  • Why the new APIs? The old SharedPreferences.getInstance() singleton cached everything in memory and had a synchronous-read footgun; SharedPreferencesAsync and SharedPreferencesWithCache replace it with clearer async semantics.
  • Not a database, not secure: it's unencrypted and unstructured — don't store tokens/secrets (use flutter_secure_storage) or large/relational data (use Drift/sqflite/Hive).
  • Defaults: a missing key returns null, so always provide a fallback (?? default).
  • It's intended for small values; abusing it as a data store hurts performance.
↑ Back to top

Follow-up 1

What type of data is typically stored using shared preferences?

Shared preferences is typically used to store simple data types such as strings, booleans, integers, and floats. It is not recommended to store large amounts of data or complex data structures using shared preferences.

Follow-up 2

What are the limitations of shared preferences?

Shared preferences has some limitations:

  • It can only store simple data types such as strings, booleans, integers, and floats.
  • The amount of data that can be stored is limited and varies depending on the platform.
  • Shared preferences is not secure and should not be used to store sensitive data.
  • It is not suitable for storing large amounts of data or complex data structures.

Follow-up 3

How would you handle data encryption with shared preferences?

To handle data encryption with shared preferences, you can use a third-party library such as flutter_secure_storage. This library provides a secure way to store sensitive data by encrypting it before storing it in shared preferences. Here is an example of how to use flutter_secure_storage:

import 'package:flutter_secure_storage/flutter_secure_storage.dart';

final storage = FlutterSecureStorage();

// Store encrypted data
await storage.write(key: 'key', value: 'value');

// Retrieve decrypted data
String value = await storage.read(key: 'key');

Follow-up 4

Can you provide an example of using shared preferences in a Flutter application?

Sure! Here is an example of how to use shared preferences in a Flutter application:

import 'package:shared_preferences/shared_preferences.dart';

// Store data
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('key', 'value');

// Retrieve data
String value = prefs.getString('key');

Follow-up 5

How does shared preferences handle data persistence across app updates?

Shared preferences handles data persistence across app updates automatically. When an app is updated, the shared preferences data is not affected and remains intact. This means that the data stored using shared preferences will still be available after an app update.

4. Can you explain how to use Hive for data persistence in Flutter?

Hive is a fast, lightweight NoSQL key-value database written in pure Dart, good for storing objects without SQL. Note the maintenance landscape: the original author stepped back, so the actively maintained fork in 2026 is Hive CE (Community Edition). Usage:

  1. Add the packages (Hive CE):
dependencies:
  hive_ce: ^2.0.0
  hive_ce_flutter: ^2.0.0
dev_dependencies:
  hive_ce_generator: ^1.0.0
  build_runner: ^2.4.0
  1. Annotate a model and generate a type adapter:
import 'package:hive_ce/hive.dart';
part 'person.g.dart';

@HiveType(typeId: 0)
class Person extends HiveObject {
  @HiveField(0) String name;
  @HiveField(1) int age;
  Person(this.name, this.age);
}

Run dart run build_runner build to generate the adapter.

  1. Initialize and register before runApp:
await Hive.initFlutter();
Hive.registerAdapter(PersonAdapter());
await Hive.openBox('people');
  1. A box behaves like a persistent map:
final box = Hive.box('people');
await box.put('p1', Person('John', 25));
final p = box.get('p1');

Interview follow-ups:

  • Box vs LazyBox: a regular box loads all values into memory on open (fast access, more RAM); a LazyBox keeps values on disk and reads each on demand — use it for large datasets.
  • Reactivity: box.watch() and ValueListenableBuilder(valueListenable: box.listenable()) rebuild UI on change.
  • Hive vs Isar vs ObjectBox: Hive is simplest key-value; Isar v4 (community) and ObjectBox add indexing/queries and higher performance. Always check fork maintenance health before committing.
  • typeId/HiveField indices are permanent — changing them breaks existing stored data; only add new fields.
↑ Back to top

Follow-up 1

What are the advantages of using Hive?

There are several advantages of using Hive for data persistence in Flutter:

  1. Performance: Hive is designed to be fast and lightweight. It uses a binary file format and memory-mapped files for efficient data storage and retrieval.

  2. No SQL: Hive is a NoSQL database, which means you don't need to write complex SQL queries to interact with the database. Instead, you can use simple key-value operations to store and retrieve data.

  3. Type Safety: Hive supports type-safe data serialization and deserialization. You can define your data models using Dart classes and annotate them with Hive annotations to ensure type safety.

  4. Platform Support: Hive supports both Android and iOS platforms, making it a cross-platform solution for data persistence in Flutter.

  5. Flutter Integration: Hive has a dedicated package for Flutter integration (hive_flutter), which provides seamless integration with Flutter widgets and state management libraries like Provider and Riverpod.

Follow-up 2

What are the limitations of Hive?

While Hive is a powerful data persistence solution for Flutter, it also has some limitations:

  1. No Relationships: Hive does not support relationships between data models out of the box. If you need to establish relationships between your data models, you'll need to handle it manually.

  2. No Indexing: Hive does not provide built-in indexing capabilities. If you need to perform complex queries or search operations on your data, you'll need to implement custom indexing logic.

  3. Limited Querying: Hive does not support complex querying operations like joins or aggregations. If you need to perform complex queries on your data, you may need to consider using a different database solution.

  4. No Encryption: Hive does not provide built-in encryption for data stored in the database. If you need to encrypt your data, you'll need to implement encryption logic manually.

Despite these limitations, Hive is still a powerful and efficient data persistence solution for many Flutter applications.

Follow-up 3

How does Hive handle complex data structures?

Hive provides several options for handling complex data structures:

  1. Type Adapters: Hive supports type adapters, which allow you to define custom serialization and deserialization logic for complex data structures. You can create a type adapter by implementing the TypeAdapter interface and registering it with Hive.

  2. Nested Boxes: Hive allows you to nest boxes within other boxes, which can be useful for organizing and managing complex data structures. You can create a nested box by calling the Hive.box() method within another box.

  3. Lazy Loading: Hive supports lazy loading of data, which means that it only loads the data from disk when it is accessed. This can be useful for optimizing performance when working with large or complex data structures.

By using these features, you can effectively handle complex data structures in Hive and store them persistently in your Flutter application.

Follow-up 4

Can you provide an example of using Hive in a Flutter application?

Sure! Here's an example of how you can use Hive for data persistence in a Flutter application:

  1. Define a data model class:
import 'package:hive/hive.dart';

part 'person.g.dart';

@HiveType(typeId: 0)
class Person extends HiveObject {
  @HiveField(0)
  String name;

  @HiveField(1)
  int age;
}
  1. Initialize Hive in your application's main function:
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';

void main() async {
  await Hive.initFlutter();
  await Hive.openBox('myBox');
  runApp(MyApp());
}
  1. Use the Hive box to store and retrieve data:
import 'package:hive/hive.dart';

void savePerson() {
  var box = Hive.box('myBox');
  var person = Person()
    ..name = 'John'
    ..age = 25;
  box.put('person', person);
}

void getPerson() {
  var box = Hive.box('myBox');
  var person = box.get('person') as Person;
  print(person.name); // Output: John
  print(person.age); // Output: 25
}

This example demonstrates how to define a Person class, initialize Hive, and use a Hive box to store and retrieve a Person object.

Follow-up 5

How does Hive compare to other data persistence techniques in Flutter?

Hive offers several advantages over other data persistence techniques in Flutter:

  1. Performance: Hive is known for its excellent performance. It is faster than SQLite and shared_preferences, especially for read-heavy operations.

  2. Simplicity: Hive provides a simple and intuitive API for data persistence. It uses key-value operations, which are easier to understand and use compared to SQL queries.

  3. Type Safety: Hive supports type-safe data serialization and deserialization. This ensures that your data is stored and retrieved correctly without any type-related issues.

  4. Flutter Integration: Hive has a dedicated package for Flutter integration (hive_flutter), which provides seamless integration with Flutter widgets and state management libraries like Provider and Riverpod.

However, it's important to note that Hive may not be suitable for all use cases. If you need complex querying capabilities, relationships between data models, or built-in encryption, you may need to consider other data persistence techniques like SQLite or Firebase Firestore.

5. How do you handle data persistence in a Flutter application with a backend?

With a backend, persistence becomes a two-tier problem: the server is the authoritative store, and the device keeps a local copy/cache so the app is fast and works offline. The architecture most teams describe in 2026 is offline-first:

  • Local store as the UI's source of truth — Drift or sqflite for relational data, Hive CE/Isar/ObjectBox for objects, shared_preferences/secure storage for small/sensitive values.
  • Remote API over REST or GraphQL (http/dio, or graphql_flutter); the app reads/writes locally first, then syncs with the server in the background.
  • Sync layer that reconciles the two — push local changes, pull remote updates, resolve conflicts.
Future> getTodos() async {
  try {
    final remote = await api.fetchTodos();   // network
    await db.upsertAll(remote);              // update local cache
  } catch (_) { /* offline → fall back to cache */ }
  return db.watchTodos().first;              // UI reads from local store
}

Interview follow-ups:

  • Conflict resolution: last-write-wins is simplest; serious apps use version/updated-at timestamps or a sync engine (PowerSync, Supabase, Firestore offline persistence).
  • Auth tokens go in flutter_secure_storage, not shared_preferences.
  • Cache invalidation / staleness: decide TTLs, optimistic updates, and how to surface sync errors and retries to the user.
  • Don't block the UI on the network — read from cache immediately, reconcile asynchronously.
↑ Back to top

Follow-up 1

What are the considerations for data synchronization between the app and the backend?

When considering data synchronization between the app and the backend, there are a few key considerations:

  1. Conflict resolution: In cases where the same data is being modified on both the app and the backend, it is important to have a conflict resolution mechanism in place. This can involve using timestamps or version numbers to determine which version of the data is the most recent.

  2. Network connectivity: Since data synchronization requires communication between the app and the backend, it is important to handle scenarios where the app may not have a stable network connection. This can involve queuing data updates and syncing them when the network connection is available.

  3. Data consistency: Ensuring data consistency between the app and the backend is crucial. This can involve validating data on both ends and implementing proper error handling mechanisms.

By considering these factors, you can ensure that data synchronization between the app and the backend is reliable and efficient.

Follow-up 2

How would you handle offline data persistence?

To handle offline data persistence in a Flutter application, you can use local storage mechanisms such as sqflite or shared_preferences. These libraries allow you to store data on the device itself, even when there is no network connectivity.

When the app is offline, you can store the data locally and queue any data updates that need to be synced with the backend. Once the network connection is available, you can then sync the locally stored data with the backend server.

Additionally, you can implement caching mechanisms to improve offline data access. This can involve caching frequently accessed data on the device, so that it can be accessed even when the app is offline.

By using these techniques, you can ensure that data is persisted and accessible even when the app is offline, providing a seamless user experience.

Follow-up 3

Can you explain how to use Firebase for data persistence in Flutter?

Yes, Firebase can be used for data persistence in Flutter applications. Firebase provides a suite of cloud-based services, including a real-time database and cloud storage, which can be used to store and sync data.

To use Firebase for data persistence in Flutter, you can follow these steps:

  1. Set up a Firebase project: Create a new project on the Firebase console and configure the necessary settings.

  2. Add the Firebase SDK to your Flutter project: Add the necessary dependencies to your Flutter project's pubspec.yaml file and run 'flutter pub get' to install the Firebase SDK.

  3. Initialize Firebase in your Flutter app: In your app's main.dart file, initialize Firebase using the Firebase.initializeApp() method.

  4. Use Firebase services for data persistence: You can now use Firebase services like the Firestore database or the Firebase Realtime Database to store and sync data in your Flutter app.

By using Firebase, you can leverage its powerful data persistence and synchronization capabilities, allowing you to build robust and scalable Flutter applications.

Follow-up 4

What are the security considerations for data persistence with a backend?

When it comes to data persistence with a backend, there are several security considerations to keep in mind:

  1. Authentication and authorization: Implement proper authentication and authorization mechanisms to ensure that only authorized users can access and modify the data. This can involve using techniques like user authentication, role-based access control, and token-based authentication.

  2. Encryption: Encrypt sensitive data before storing it in the backend to protect it from unauthorized access. This can involve using encryption algorithms like AES or RSA.

  3. Input validation: Validate user input to prevent common security vulnerabilities like SQL injection or cross-site scripting (XSS) attacks. This can involve using input validation libraries or implementing custom validation logic.

  4. Regular security audits: Regularly audit the security of your backend infrastructure and data storage to identify and address any vulnerabilities.

By considering these security measures, you can ensure that data persistence with a backend is secure and protected from unauthorized access.

Follow-up 5

How would you handle data conflicts between the app and the backend?

Handling data conflicts between the app and the backend involves implementing a conflict resolution mechanism. Here are some approaches to handle data conflicts:

  1. Timestamp-based conflict resolution: Assign a timestamp to each data update and use it to determine the most recent version of the data. When a conflict occurs, compare the timestamps and choose the version with the latest timestamp as the winner.

  2. Version number-based conflict resolution: Assign a version number to each data update and use it to determine the most recent version of the data. When a conflict occurs, compare the version numbers and choose the version with the highest number as the winner.

  3. Manual conflict resolution: In some cases, conflicts may require manual intervention. You can implement a mechanism to notify the user about the conflict and allow them to choose the desired version of the data.

By implementing a conflict resolution mechanism, you can ensure that data conflicts between the app and the backend are handled in a controlled and consistent manner.

Live mock interview

Mock interview: Data Persistence Techniques

Intermediate ~5 min Your own free AI key

Your voice and your AI key never touch our servers; the key stays in this browser and is sent only to Google. Only your round scores are saved to track progress.