DEV Community

Cover image for Flutter Offline Data Storage: Exploring Differents Solutions
7oby
7oby

Posted on

Flutter Offline Data Storage: Exploring Differents Solutions

Flutter’s out here flexing as one of the top, if not THE top, frameworks for building cross-platform mobile apps. It makes it easy to create apps that perform at their best and keep your users happy with smooth experiences.

Now, when we talk about user experience, there’s one thing that really matters — making sure your app works even when users are offline. Yeah, you need to make sure they can still access the important stuff in your app even when the Wi-Fi’s acting up or they’re somewhere with bad signal.

If you didn’t know, adding offline features to your app is gonna:

  1. Boost the User Experience: Users can still get into your app and use its features without needing a strong internet connection. It makes your app more reliable.

  2. Make Your App Faster: Storing data locally means your app loads up quicker and runs smoother.

  3. Help Users in Low Connectivity Areas: People with bad or no internet still get to use your app, which makes it super useful in a bunch of places.

  4. Cut Data Costs: Users save on data by using stored info instead of constantly re-downloading stuff.

  5. Keep Things Running Smooth: Apps like note-taking or e-commerce can still run without any interruptions — users can add, edit, and save stuff while offline.

  6. Get People Coming Back: If your app works well offline, users are more likely to stick around and use it more.

  7. Make It Reliable: Offline mode means less chance of interruptions, so users can count on your app when they need it most.

  8. Give Users More Freedom: They can use the app whenever, whether they’re traveling, in a meeting, or in a spot with bad connection.

There’re many ways to handle offline storage in Flutter. From simple stuff like key-value pairs with Shared Preferences to full-on databases like SQLite, or lightweight NoSQL ones like Hive and ObjectBox.

In this article, we’re gonna give you a quick rundown on each. But trust, we’ll dive deeper into them in upcoming posts — explaining what makes each of them tick and when to use ‘em.

So, let’s jump right in…

Shared Preferences Package

Shared Preferences is like a simple locker where you can store small data, like settings or app states. It’s perfect for lightweight storage, like saving the user’s theme, language settings, or login tokens. It’s easy to set up and doesn’t require you to deal with complicated databases.

Here’s how you use it:

import 'package:shared_preferences/shared_preferences.dart';

// Save a value
Future<void> savePreference(String key, String value) async {
 final prefs = await SharedPreferences.getInstance();
 await prefs.setString(key, value);
}

// Get a value
Future<String?> getPreference(String key) async {
 final prefs = await SharedPreferences.getInstance();
 return prefs.getString(key);
}
Enter fullscreen mode Exit fullscreen mode

Pros:

  • Easy to set up for storing simple stuff.
  • Lightweight and fast.
  • Works cross-platform with a consistent API.

Cons:

  • Not built for complex data or big datasets.
  • Only stores basic types like strings, ints, and booleans.
  • No fancy query options for more advanced needs.

SQLite

SQLite is like a mini database you can embed directly into your app. It’s great for handling large, structured data with relationships. It’s good when you need to manage a lot of data and want to run SQL queries to fetch and manipulate it.

Check this out for basic SQLite operations:

import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

// Setup the DB
Future<Database> initializeDB() async {
 String path = join(await getDatabasesPath(), 'example.db');
 return openDatabase(
   path,
   onCreate: (database, version) async {
     await database.execute(
       "CREATE TABLE items(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)",
     );
   },
   version: 1,
 );
}

// Insert a new item
Future<void> insertItem(String name) async {
 final Database db = await initializeDB();
 await db.insert('items', {'name': name});
}

// Get all items
Future<List<Map<String, dynamic>>> retrieveItems() async {
 final Database db = await initializeDB();
 return await db.query('items');
}

Enter fullscreen mode Exit fullscreen mode

Pros:

  • Good for complex queries and handling relationships between data.
  • Handles big data sets well.
  • Transactions keep your data safe and secure.

Cons:

  • A little more setup than simple storage options.
  • Not needed if you only need to store small amounts of data.
  • You gotta know SQL, which might be a challenge for beginners.

Hive

Hive is a fast and simple NoSQL database made for Flutter. It’s light, quick, and great for storing data without the need for SQL. You can store basic data types, lists, and maps, and it handles read/write operations like a champ.

Here’s how you can get started with Hive:

import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';

void main() async {
 await Hive.initFlutter();
 var box = await Hive.openBox('myBox');
// Save data
 await box.put('name', 'Flutter Developer');
// Retrieve data
 String name = box.get('name') ?? 'No Name';
 print(name); // Output: Flutter Developer
}

Enter fullscreen mode Exit fullscreen mode

Pros:

  • Super fast, making it great for performance-sensitive apps.
  • Simple and easy to use.
  • Supports lists and maps, making it more flexible.

Cons:

  • No relational features like SQLite.
  • Changing your data structure can be tricky.
  • Limited querying options.

Drift (Moor)

Drift (previously Moor) is like a turbocharged SQLite. It lets you work with SQL while adding cool features like real-time updates and automatic code generation. It’s great for apps with structured data and when you need your app to react to data changes in real-time.

Here’s how you set it up:

import 'package:drift/drift.dart';
import 'package:drift/native.dart';

// Define the structure
@DataClassName('Item')
class Items extends Table {
 IntColumn get id => integer().autoIncrement()();
 TextColumn get name => text()();
}

// Create the database
@DriftDatabase(tables: [Items])
class AppDatabase extends _$AppDatabase {
 AppDatabase() : super(NativeDatabase.memory());
 Future<List<Item>> getAllItems() => select(items).get();
 Future insertItem(Insertable<Item> item) => into(items).insert(item);
}

Enter fullscreen mode Exit fullscreen mode

Pros:

  • Type-safe, so it avoids bugs when working with data.
  • Real-time updates, so the app UI stays fresh.
  • Built-in migrations to help with schema changes.

Cons:

  • Takes some time to get used to if you’re new to reactive programming.
  • Setup can be more complex than simpler storage methods.
  • Code generation and extra dependencies add a bit of overhead.

ObjectBox

ObjectBox is a high-performance NoSQL database built for mobile and IoT apps. It’s fast as hell, and you work directly with Dart objects, so there’s no need to convert stuff into JSON. It’s ideal for real-time apps or anything needing quick access to a lot of data.

Here’s how you set it up:

import 'package:objectbox/objectbox.dart';
import 'package:objectbox_flutter_libs/objectbox_flutter_libs.dart';

// Define the data model
@Entity()
class Item {
 int id;
 String name;
 Item({this.id = 0, required this.name});
}

// Create the ObjectBox store
void main() async {
 final store = await openStore();
// Save an object
 final item = Item(name: 'Flutter Developer');
 store.box<Item>().put(item);
// Get all items
 final items = store.box<Item>().getAll();
 items.forEach((item) {
   print('Item: ${item.name}');
 });
}

Enter fullscreen mode Exit fullscreen mode

Pros:

  • Super fast for large datasets.
  • Directly works with Dart objects — no conversion needed.
  • Built-in reactive programming support.

Cons:

  • NoSQL, so not ideal for complex queries.
  • Limited docs and community support.
  • Might take time to get used to if you’re not familiar with object-oriented databases.

Final Thoughts

When it comes to choosing offline storage in Flutter, here’s what I recommend:

  • For simple preferences and settings: Go with Shared Preferences.
  • For fast data storage: Try Hive.
  • For structured data with relationships: Go for Drift (Moor).
  • For high-performance apps: ObjectBox is the move.
  • For complex data and queries: SQLite is the best.

At the end of the day, it all comes down to what kind of app you’re building. Choose wisely based on your needs, and you’ll be good to go.

Follow me! And stay tuned for future posts where we’ll break each of these options down in detail and show you exactly how to integrate them into your apps.

Top comments (0)