Popular Searches
Popular Course Categories
Popular Courses

Local Data Storage

Flutter Packages & Local Storage


Local Data Storage in Flutter


Local Data Storage means saving application data directly on the user's device so that the data can be accessed later without requesting it again from a remote server. Flutter provides several approaches for local persistence depending on the type and amount of data that needs to be stored.


Local storage is useful for saving user preferences, login states, application settings, cached information, offline data, files, and structured records. Flutter's persistence options include key-value storage, local files, and SQLite databases.




1. What is Local Data Storage?


Local Data Storage is the process of storing information on the device where the Flutter application is installed.


For example, an application may need to remember:



  • Whether the user has completed onboarding.

  • The selected language.

  • Dark mode or light mode preference.

  • A login or session-related flag.

  • Shopping cart information.

  • Recently viewed data.

  • Offline application data.

  • Documents, images, text files, or other local files.


Simple Flow


User Action
    ↓
Flutter Application
    ↓
Local Storage Layer
    ↓
Device Storage
    ↓
Data remains available for later use



2. Why Use Local Data Storage?



  • It allows data to remain available after the application is closed.

  • It can reduce unnecessary network requests.

  • It improves application responsiveness.

  • It supports offline functionality.

  • It can store user preferences and application configuration.

  • It can cache information received from an API.

  • It provides a convenient way to persist application state.


3. Types of Local Storage in Flutter









Storage MethodBest Used ForExample
SharedPreferencesSmall key-value dataTheme, language, onboarding status
File StorageFiles and text dataJSON, text files, downloaded documents
SQLiteStructured and relational dataProducts, users, orders, tasks
Secure StorageSensitive valuesTokens and credentials
CacheTemporary/reusable dataImages and API responses

The correct storage method depends on the type, size, structure, lifetime, and security requirements of the data.




4. SharedPreferences


shared_preferences is commonly used when an application needs to store a relatively small collection of key-value pairs. Flutter's documentation describes it as a solution for simple persistent key-value data.


Common Use Cases



  • Dark mode preference

  • Language preference

  • Onboarding completion

  • Simple application settings

  • Small counters

  • Simple boolean flags

  • Non-sensitive user preferences


Install SharedPreferences


flutter pub add shared_preferences

Import Package


import 'package:shared_preferences/shared_preferences.dart';

Supported Data Types









TypeExampleMethod
StringManishsetString()
int25setInt()
double99.50setDouble()
booltruesetBool()
List["Flutter","Dart"]setStringList()

Save String Data


final prefs = await SharedPreferences.getInstance();

await prefs.setString('username', 'Manish');


Read String Data


final prefs = await SharedPreferences.getInstance();

String username = prefs.getString('username') ?? 'Guest';

print(username);


Save Integer


await prefs.setInt('age', 25);

Read Integer


int age = prefs.getInt('age') ?? 0;

Save Boolean


await prefs.setBool('isLoggedIn', true);

Read Boolean


bool isLoggedIn = prefs.getBool('isLoggedIn') ?? false;

Save Double


await prefs.setDouble('price', 499.99);

Read Double


double price = prefs.getDouble('price') ?? 0.0;

Save List


await prefs.setStringList(
  'skills',
  ['Flutter', 'Dart', 'Firebase'],
);

Read List


List skills =
    prefs.getStringList('skills') ?? [];



5. Complete SharedPreferences Example


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

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: const StoragePage(),
    );
  }
}

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

  @override
  State createState() => _StoragePageState();
}

class _StoragePageState extends State {
  String username = '';

  Future saveData() async {
    final prefs = await SharedPreferences.getInstance();

    await prefs.setString('username', 'Manish');

    setState(() {
      username = 'Manish';
    });
  }

  Future loadData() async {
    final prefs = await SharedPreferences.getInstance();

    setState(() {
      username = prefs.getString('username') ?? 'No Data';
    });
  }

  Future removeData() async {
    final prefs = await SharedPreferences.getInstance();

    await prefs.remove('username');

    setState(() {
      username = 'Data Removed';
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Local Storage'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          children: [
            Text(
              'Username: $username',
              style: const TextStyle(fontSize: 20),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: saveData,
              child: const Text('Save Data'),
            ),
            ElevatedButton(
              onPressed: loadData,
              child: const Text('Load Data'),
            ),
            ElevatedButton(
              onPressed: removeData,
              child: const Text('Remove Data'),
            ),
          ],
        ),
      ),
    );
  }
}




6. Removing Local Data


The remove() method removes a particular key and its associated value.


final prefs = await SharedPreferences.getInstance();

await prefs.remove('username');


Check Whether a Key Exists


final prefs = await SharedPreferences.getInstance();

if (prefs.containsKey('username')) {
  print('Username exists');
}


Clear Stored Preferences


final prefs = await SharedPreferences.getInstance();

await prefs.clear();


Use clear() carefully because it removes all stored preference values managed by that preference store.




7. Login Status Example


A simple login state can be stored as a boolean value.


final prefs = await SharedPreferences.getInstance();

await prefs.setBool('isLoggedIn', true);


Later, the application can read the value:


final prefs = await SharedPreferences.getInstance();

bool isLoggedIn = prefs.getBool('isLoggedIn') ?? false;

if (isLoggedIn) {
  print('User is logged in');
} else {
  print('User is logged out');
}


Important


A simple preference flag is not the same as securely storing authentication credentials. Passwords, private keys, and other sensitive secrets should not be stored as ordinary SharedPreferences values.




8. Onboarding Example


Many applications display onboarding screens only the first time the application is opened.


final prefs = await SharedPreferences.getInstance();

bool completed =
    prefs.getBool('onboardingCompleted') ?? false;


After the user finishes onboarding:


await prefs.setBool('onboardingCompleted', true);

On the next application launch, the application can check this value and decide whether to display onboarding or the main screen.




9. Theme Preference


Local storage can remember whether the user selected dark mode.


final prefs = await SharedPreferences.getInstance();

await prefs.setBool('darkMode', true);


Read the value:


final prefs = await SharedPreferences.getInstance();

bool darkMode = prefs.getBool('darkMode') ?? false;


This type of key-value storage is appropriate for simple application configuration such as theme settings.




10. Storing JSON Data Locally


SharedPreferences is designed for simple supported types. If an application needs to store a small object, the object can be converted to JSON and then stored as a String.


Convert Object to JSON


import 'dart:convert';

Map user = {
  'name': 'Manish',
  'age': 25,
  'city': 'Mumbai',
};

final prefs = await SharedPreferences.getInstance();

await prefs.setString(
  'user',
  jsonEncode(user),
);


Read JSON Data


final prefs = await SharedPreferences.getInstance();

String? data = prefs.getString('user');

if (data != null) {
  Map user = jsonDecode(data);

  print(user['name']);
  print(user['age']);
}


Important


JSON stored in a key-value store can be useful for small objects, but it is not a replacement for a proper database when the application contains large or complex datasets.




11. File Storage


When an application needs to store actual files or larger text content, file storage can be used. Flutter's path_provider package provides commonly used filesystem locations, while Dart's dart:io provides the File API for reading and writing files on supported mobile and desktop platforms.


Install path_provider


flutter pub add path_provider

Import Packages


import 'dart:io';
import 'package:path_provider/path_provider.dart';

Get Application Documents Directory


Future getApplicationDirectory() async {
  return await getApplicationDocumentsDirectory();
}

Write a File


Future saveFile() async {
  final directory =
      await getApplicationDocumentsDirectory();

  final file = File('${directory.path}/data.txt');

  await file.writeAsString('Hello Flutter');
}


Read a File


Future readFile() async {
  final directory =
      await getApplicationDocumentsDirectory();

  final file = File('${directory.path}/data.txt');

  if (await file.exists()) {
    return await file.readAsString();
  }

  return '';
}


File Storage Use Cases



  • Saving text documents

  • Saving generated files

  • Storing downloaded content

  • Saving JSON files

  • Offline documents

  • Application-generated reports




12. SQLite Database


When the application needs structured data, relationships, filtering, sorting, and database queries, SQLite is a suitable local persistence approach.


Flutter documentation demonstrates SQLite persistence using the sqflite package. It is particularly useful for applications that need to store and query larger amounts of structured data.


Install Packages


flutter pub add sqflite path

Import Packages


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

Open a Database


final database = await openDatabase(
  join(
    await getDatabasesPath(),
    'app_database.db',
  ),
  version: 1,
);

Create a Table


final database = await openDatabase(
  join(
    await getDatabasesPath(),
    'app_database.db',
  ),
  version: 1,
  onCreate: (db, version) async {
    await db.execute(
      'CREATE TABLE users('
      'id INTEGER PRIMARY KEY, '
      'name TEXT, '
      'email TEXT'
      ')',
    );
  },
);

Insert Data


await database.insert(
  'users',
  {
    'name': 'Manish',
    'email': '[email protected]',
  },
);

Read Data


final users = await database.query('users');

for (final user in users) {
  print(user);
}


Update Data


await database.update(
  'users',
  {
    'name': 'Manish Negi',
  },
  where: 'id = ?',
  whereArgs: [1],
);

Delete Data


await database.delete(
  'users',
  where: 'id = ?',
  whereArgs: [1],
);



13. SharedPreferences vs File Storage vs SQLite










FeatureSharedPreferencesFile StorageSQLite
Data TypeSimple key-valueFiles/textStructured records
Complex QueriesNoNoYes
RelationshipsNoNoYes
Best ForSettings and preferencesDocuments and filesLarge structured data
ExampleDark modePDF/documentProduct database
SQL SupportNoNoYes



14. Choosing the Correct Storage Method


What type of data do you need to store?
            |
            +-- Simple key-value?
            |       |
            |       +-- Use SharedPreferences
            |
            +-- Files or text?
            |       |
            |       +-- Use File Storage
            |
            +-- Structured records?
                    |
                    +-- Use SQLite / Database

Example Decision












RequirementRecommended Approach
Dark mode settingSharedPreferences
Selected languageSharedPreferences
Onboarding completed flagSharedPreferences
Downloaded text fileFile Storage
Generated PDFFile Storage
Thousands of productsSQLite
Orders and customersSQLite
Relational dataSQLite



15. Local Storage and Offline Applications


Local storage plays an important role in offline-first applications. An application can save information locally and display it even when the network is temporarily unavailable.


Typical Offline Flow


Internet Available
       ↓
Fetch Data from API
       ↓
Save Data Locally
       ↓
Display Data
       ↓
Internet Unavailable
       ↓
Read Cached/Stored Data
       ↓
Display Previously Saved Data

This approach can improve the user experience because the application does not always have to wait for a network response before displaying previously available information.




16. Data Storage Service


Instead of directly calling SharedPreferences from every widget, it is often cleaner to create a separate service class responsible for local storage.


Example Storage Service


import 'package:shared_preferences/shared_preferences.dart';

class StorageService {
  Future saveUsername(String username) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString('username', username);
  }

  Future getUsername() async {
    final prefs = await SharedPreferences.getInstance();
    return prefs.getString('username') ?? '';
  }

  Future removeUsername() async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.remove('username');
  }
}


Why Create a Service?



  • It keeps storage logic separate from UI code.

  • It improves code organization.

  • It makes code easier to reuse.

  • It simplifies testing.

  • It makes it easier to replace the underlying storage implementation later.




17. Repository Pattern for Local Storage


For larger applications, a repository can act as the application's interface to stored data.


UI
 ↓
ViewModel / Controller
 ↓
Repository
 ↓
Local Storage Service
 ↓
SharedPreferences / SQLite / File Storage

This architecture prevents UI components from becoming tightly coupled to a particular storage package.




18. Important Security Considerations


Local storage should not automatically be considered secure storage.


Avoid storing highly sensitive information in ordinary key-value storage without appropriate protection.



  • Do not store plain-text passwords.

  • Do not store private encryption keys in ordinary preferences.

  • Do not assume local data cannot be accessed on a compromised device.

  • Use an appropriate secure-storage solution for sensitive credentials.

  • Store only the data that the application actually needs.




19. Common Mistakes


Mistake 1: Using SharedPreferences for Large Data


SharedPreferences is intended for relatively small key-value data, not large databases.


Mistake 2: Storing Passwords Directly


Ordinary local preferences should not be treated as a secure password vault.


Mistake 3: Forgetting Async Operations


Many local-storage operations are asynchronous and should be handled with async and await.


Future saveData() async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setString('name', 'Manish');
}

Mistake 4: Using Incorrect Getter Types


The type used for reading should correspond to the type used when storing the value.


await prefs.setInt('age', 25);

int age = prefs.getInt('age') ?? 0;


Mistake 5: Mixing Storage Logic with UI


For larger applications, separate storage operations into services or repositories instead of putting all persistence code directly inside widgets.




20. Best Practices



  • Choose the storage mechanism according to the data requirements.

  • Use SharedPreferences for simple preferences and small key-value values.

  • Use files for file-oriented data.

  • Use SQLite for structured and queryable data.

  • Keep storage operations outside complex UI widgets when possible.

  • Use meaningful and consistent storage keys.

  • Handle missing values with sensible defaults.

  • Handle asynchronous operations correctly.

  • Do not store passwords as ordinary preferences.

  • Do not use a key-value store as a replacement for a relational database.

  • Test local storage operations.

  • Consider offline requirements while designing the data layer.




21. Practical Project Example


Consider a Flutter shopping application.











DataStorage
Dark modeSharedPreferences
Selected languageSharedPreferences
Onboarding statusSharedPreferences
Recently viewed productsSharedPreferences or local database depending on size
Product catalogSQLite/local database for substantial structured data
Downloaded invoicesFile Storage
Authentication secretsSecure storage solution

Application Flow


Flutter Shopping App
        |
        +-- User Preferences
        |       ↓
        |   SharedPreferences
        |
        +-- Product Data
        |       ↓
        |   Local Database
        |
        +-- Downloaded Invoice
        |       ↓
        |   File Storage
        |
        +-- Sensitive Credentials
                ↓
           Secure Storage



22. Local Data Storage Interview Questions


Q1. What is local data storage?


Local data storage is the process of saving application data on the user's device so it can be accessed later.


Q2. What is SharedPreferences used for?


It is used for relatively small key-value data such as preferences, settings, flags, and simple application configuration.


Q3. What data types can SharedPreferences store?


Common supported types are int, double, bool, String, and List.


Q4. When should SQLite be used?


SQLite is appropriate when the application needs structured, queryable, and potentially larger amounts of local data.


Q5. When should file storage be used?


File storage is useful when the application needs to save documents, text, JSON files, generated files, or other file-based content.


Q6. Can SharedPreferences replace SQLite?


No. SharedPreferences is intended for simple key-value storage, while SQLite is designed for structured data and database queries.


Q7. Why should passwords not be stored in ordinary SharedPreferences?


Ordinary preference storage should not be treated as a secure credential store. Sensitive credentials require an appropriate secure-storage mechanism.


Q8. What is offline storage?


Offline storage allows an application to keep information locally so that previously stored data can be accessed when the network is unavailable.




23. Summary



  • Local Data Storage allows Flutter applications to persist information on the device.

  • SharedPreferences is suitable for relatively small key-value data.

  • File storage is useful for documents and file-based data.

  • SQLite is useful for structured and queryable local data.

  • Local storage can support offline application functionality.

  • Storage operations should be handled asynchronously where required.

  • Sensitive information should use an appropriate secure-storage mechanism.

  • For larger applications, services and repositories can separate storage logic from the UI.




24. Learn More About Flutter


JustAcademy Flutter Training Course


Register for Flutter Course Demo


whatsapp