flutter
/

GetX Storage: Lightweight Key-Value Storage with GetStorage

Last Sync: Today

On this page

14
0%
5 min read
Remaining
5 minleft

Click any section to jump — progress syncs automatically

flutter

GetX Storage: Lightweight Key-Value Storage with GetStorage

What is GetStorage?

GetStorage is a simple, fast, and lightweight key-value storage solution that comes with GetX. It stores data in a JSON file on the device (Android, iOS, Web, Desktop) and provides a synchronous API with reactive capabilities. It's perfect for storing user preferences, app settings, small caches, or any small amount of data that needs to persist across app launches.

Why Use GetStorage?

  • No context needed – Can be used anywhere in your app, including controllers and services.
  • Simple API – write, read, delete, hasData – very straightforward.
  • Reactive – GetStorage can be observed with .obs or workers, so UI updates automatically.
  • Lightweight – Stores data in a single JSON file; no complex setup.
  • Cross-platform – Works on Android, iOS, Web, macOS, Windows, Linux.
  • Synchronous – No async/await for simple operations (though writing is async behind the scenes).

Installation

GetStorage is part of the GetX package. Just add GetX to your pubspec.yaml.

YAMLRead-only
1
dependencies:
  get: ^4.6.6

Initialization

Before using GetStorage, you need to initialize it. It's recommended to do this in main() before runApp.

DARTRead-only
1
void main() async {
  await GetStorage.init(); // initializes default storage
  runApp(MyApp());
}

You can also create named storage boxes by passing a name.

DARTRead-only
1
await GetStorage.init('settings'); // creates a separate box

Basic Usage

DARTRead-only
1
final box = GetStorage();
box.write('username', 'john_doe');
box.write('isLogged', true);
box.write('age', 25);
DARTRead-only
1
final username = box.read('username'); // returns dynamic
final isLogged = box.read('isLogged') ?? false;
final age = box.read('age');
DARTRead-only
1
if (box.hasData('username')) {
  // do something
}
DARTRead-only
1
box.remove('username');

// Clear all data in the box
box.erase();

Working with Named Boxes

You can create multiple isolated storage boxes using names. This helps organize data by module.

DARTRead-only
1
final userBox = GetStorage('user');
userBox.write('name', 'Alice');

final appBox = GetStorage('app');
appBox.write('theme', 'dark');

Reactive Storage

GetStorage can be observed just like any reactive variable. You can make a GetStorage instance observable by calling .obs or use workers to react to changes.

DARTRead-only
1
class SettingsController extends GetxController {
  final box = GetStorage();
  var themeMode = 'light'.obs;

  @override
  void onInit() {
    super.onInit();
    // Load saved value
    themeMode.value = box.read('theme') ?? 'light';
    // Listen to changes
    ever(themeMode, (mode) {
      box.write('theme', mode);
    });
  }

  void setTheme(String mode) => themeMode.value = mode;
}

Using GetStorage with GetBuilder/Obx

DARTRead-only
1
// In your view
class SettingsPage extends GetView<SettingsController> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Obx(() => Text('Theme: ${controller.themeMode}')),
      ),
    );
  }
}

Storage Service with GetxService

For better organization, encapsulate storage logic in a GetxService that can be used throughout the app.

DARTRead-only
1
class StorageService extends GetxService {
  late final GetStorage _box;

  Future<StorageService> init() async {
    await GetStorage.init();
    _box = GetStorage();
    return this;
  }

  void write(String key, dynamic value) => _box.write(key, value);
  T? read<T>(String key) => _box.read(key);
  bool hasData(String key) => _box.hasData(key);
  void remove(String key) => _box.remove(key);
  void clear() => _box.erase();
}

// In main
void main() async {
  await GetStorage.init();
  await Get.putAsync<StorageService>(() => StorageService().init());
  runApp(MyApp());
}

Comparison with Other Storage Options

Best Practices

  • Initialize in main – Call await GetStorage.init() before runApp to ensure storage is ready.
  • Use named boxes for modules – Keep data separate (e.g., user, app, cache).
  • Encapsulate storage in services – Makes it easier to test and change underlying storage later.
  • Use reactive patterns – Combine with Obx for automatic UI updates when preferences change.
  • Provide defaults – Use the ?? operator when reading to avoid null errors.
  • Avoid storing large data – GetStorage is not meant for large files or complex relational data; use SQLite or Hive for that.

Common Mistakes

  • ❌ Forgetting to initialize – Using GetStorage() before init() will throw an exception. ✅ Always call GetStorage.init() first.
  • ❌ Using read without checking existence – Can return null; handle with ?? or hasData.
  • ❌ Storing custom objects directly – GetStorage only supports primitive types and lists/maps. Convert custom objects to Map first.
  • ❌ Using async/await unnecessarily – write is actually synchronous (it writes to disk on a separate thread), so you don't need to await it unless you want to ensure it's written before proceeding.

FAQ

  • Q: Is GetStorage persistent?
    A: Yes, it persists data across app restarts. Data is stored in a JSON file on the device.
  • Q: Can I store custom objects?
    A: Not directly. You need to convert them to Map (e.g., {'name': user.name, 'age': user.age}) and then store. When reading, convert back.
  • Q: How do I listen to changes in GetStorage?
    A: Wrap the storage read in an Obx or use workers on a reactive variable that holds the value. The storage itself doesn't have listeners, but you can combine it with reactive variables.
  • Q: What's the maximum storage size?
    A: It's limited by the file system, but for large data (e.g., >10MB) consider using Hive or SQLite.
  • Q: Can I use GetStorage on the web?
    A: Yes, it uses localStorage on web, and the API is identical.
  • Q: How to delete all data from a box?
    A: Use box.erase().

Conclusion

GetStorage is a simple, powerful solution for persistent key-value storage in Flutter. It integrates seamlessly with GetX's state management and dependency injection, making it easy to manage app preferences and cached data. With its straightforward API and reactive capabilities, it's an excellent choice for most Flutter apps.

Try it yourself

import 'package:flutter/material.dart';
import 'package:get/get.dart';

void main() async {
  await GetStorage.init();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      home: StorageDemo(),
    );
  }
}

class SettingsController extends GetxController {
  final box = GetStorage();
  var username = ''.obs;

  @override
  void onInit() {
    super.onInit();
    username.value = box.read('username') ?? '';
  }

  void saveUsername(String name) {
    username.value = name;
    box.write('username', name);
  }

  void clearUsername() {
    username.value = '';
    box.remove('username');
  }
}

class StorageDemo extends StatelessWidget {
  final controller = Get.put(SettingsController());
  final TextEditingController textController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('GetStorage Demo')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Obx(() => Text(
              'Stored username: ${controller.username.value.isEmpty ? 'Not set' : controller.username.value}',
              style: TextStyle(fontSize: 20),
            )),
            SizedBox(height: 20),
            TextField(
              controller: textController,
              decoration: InputDecoration(labelText: 'Enter username'),
            ),
            SizedBox(height: 10),
            ElevatedButton(
              onPressed: () {
                if (textController.text.isNotEmpty) {
                  controller.saveUsername(textController.text);
                  textController.clear();
                }
              },
              child: Text('Save'),
            ),
            ElevatedButton(
              onPressed: controller.clearUsername,
              child: Text('Clear'),
            ),
          ],
        ),
      ),
    );
  }
}

Test Your Knowledge

Q1
of 3

Which method is used to initialize GetStorage?

A
GetStorage.init()
B
GetStorage.load()
C
GetStorage.prepare()
D
GetStorage.start()
Q2
of 3

How do you write a value to GetStorage?

A
box.set('key', value)
B
box.write('key', value)
C
box.put('key', value)
D
box.add('key', value)
Q3
of 3

What should you do before using GetStorage in your app?

A
Call GetStorage.initialize()
B
Await GetStorage.init()
C
Import 'package:get_storage/get_storage.dart'
D
Nothing, it's ready to use

Previous

getx services

Next

getx clean architecture

Related Content

Need help?

Explore our comprehensive docs or start a chat with our tech experts.