image

18 Jan 2026

9K

35K

Flutter & Shared Preferences: Storing Temporary Form Data

Developing applications often involves collecting user input through forms. A common challenge arises when users navigate away from a form or the app unexpectedly closes before submission: the entered data is lost. For critical input or lengthy forms, this can lead to a frustrating user experience. This article explores how to leverage Flutter's shared_preferences package to temporarily store form data, ensuring a seamless experience by allowing users to resume where they left off.

The Need for Temporary Form Data Storage

Consider a multi-step registration form or a complex settings page. If the user accidentally closes the app or switches to another application, all their progress is typically wiped clean. While a backend database is the ultimate destination for submitted data, for unsaved, in-progress data, a lightweight client-side storage solution is ideal. This is where shared_preferences shines.

Introducing shared_preferences

The shared_preferences package in Flutter provides a persistent, lightweight key-value store. It's essentially a wrapper around platform-specific storage mechanisms:

  • iOS: NSUserDefaults
  • Android: SharedPreferences
  • Web: localStorage

It's perfect for storing simple data types like strings, integers, booleans, doubles, and lists of strings. Crucially, it's not designed for large, complex datasets or relational data; for those, solutions like SQLite (via sqflite) or Hive are more appropriate. However, for temporary form data or user preferences, it's an excellent choice.

Setting Up shared_preferences

First, you need to add the package to your Flutter project's pubspec.yaml file:


dependencies:
  flutter:
    sdk: flutter
  shared_preferences: ^2.2.0 # Use the latest stable version

After adding the dependency, run flutter pub get in your terminal to fetch the package.

Saving Form Data Temporarily

To save data, you first need to obtain an instance of SharedPreferences and then use its various setter methods based on the data type. Let's imagine a simple form collecting a username and email.


import 'package:shared_preferences/shared_preferences.dart';

Future saveTemporaryFormData(String userName, String userEmail) async {
  final SharedPreferences prefs = await SharedPreferences.getInstance();
  await prefs.setString('tempUserName', userName);
  await prefs.setString('tempUserEmail', userEmail);
  print('Temporary form data saved successfully!');
}

In this example, 'tempUserName' and 'tempUserEmail' are the keys used to identify your data. It's good practice to use descriptive keys, especially when dealing with temporary data to avoid conflicts with permanent preferences.

Retrieving Stored Form Data

When the user revisits the form, you'll want to pre-fill the fields with the temporarily saved data. Retrieval is just as straightforward:


import 'package:shared_preferences/shared_preferences.dart';

Future> loadTemporaryFormData() async {
  final SharedPreferences prefs = await SharedPreferences.getInstance();
  final String? userName = prefs.getString('tempUserName');
  final String? userEmail = prefs.getString('tempUserEmail');
  print('Loaded temporary form data: Name=$userName, Email=$userEmail');
  return {'userName': userName, 'userEmail': userEmail};
}

Notice that getString() returns a nullable String?. This is because the key might not exist (e.g., it's the first time the user opens the form, or the data has been cleared), so you must handle potential null values.

Clearing Temporary Data

Once the form data is successfully submitted to your backend, or if the user explicitly cancels, you'll likely want to remove the temporary data. This prevents stale information from reappearing. You can remove individual keys or clear all preferences:


import 'package:shared_preferences/shared_preferences.dart';

Future clearTemporaryFormData() async {
  final SharedPreferences prefs = await SharedPreferences.getInstance();
  await prefs.remove('tempUserName');
  await prefs.remove('tempUserEmail');
  // Or to clear all stored preferences:
  // await prefs.clear();
  print('Temporary form data cleared!');
}

Using remove(key) is generally preferred for temporary form data to avoid inadvertently deleting other user preferences or settings stored with shared_preferences.

Integrating with a Flutter Form Widget

Let's put it all together in a simple Flutter widget. We'll use a StatefulWidget to manage the form state and TextEditingControllers to interact with the text fields.


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(
      title: 'Form Data Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const FormPage(),
    );
  }
}

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

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

class _FormPageState extends State {
  final TextEditingController _nameController = TextEditingController();
  final TextEditingController _emailController = TextEditingController();

  @override
  void initState() {
    super.initState();
    _loadFormData();
  }

  @override
  void dispose() {
    _saveFormData(); // Save data automatically when the widget is disposed
    _nameController.dispose();
    _emailController.dispose();
    super.dispose();
  }

  Future _loadFormData() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    setState(() {
      _nameController.text = prefs.getString('tempUserName') ?? '';
      _emailController.text = prefs.getString('tempUserEmail') ?? '';
    });
  }

  Future _saveFormData() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    await prefs.setString('tempUserName', _nameController.text);
    await prefs.setString('tempUserEmail', _emailController.text);
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('Form data saved temporarily!')),
    );
  }

  Future _submitFormData() async {
    // Simulate API call or database save
    await Future.delayed(const Duration(seconds: 1));
    print('Submitting Name: ${_nameController.text}, Email: ${_emailController.text}');

    // After successful submission, clear temporary data
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    await prefs.remove('tempUserName');
    await prefs.remove('tempUserEmail');

    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('Form submitted and temporary data cleared!')),
    );
    // Optionally clear controllers after submission
    _nameController.clear();
    _emailController.clear();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Temporary Form Data'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            TextField(
              controller: _nameController,
              decoration: const InputDecoration(
                labelText: 'Name',
                border: OutlineInputBorder(),
              ),
            ),
            const SizedBox(height: 20),
            TextField(
              controller: _emailController,
              decoration: const InputDecoration(
                labelText: 'Email',
                border: OutlineInputBorder(),
              ),
              keyboardType: TextInputType.emailAddress,
            ),
            const SizedBox(height: 30),
            ElevatedButton(
              onPressed: _submitFormData,
              child: const Text('Submit Form'),
            ),
            const SizedBox(height: 10),
            ElevatedButton(
              onPressed: _saveFormData,
              child: const Text('Save Form Temporarily'),
            ),
          ],
        ),
      ),
    );
  }
}

In this example:

  • _loadFormData is called in initState to pre-fill the form when it loads.
  • _saveFormData is called in dispose, ensuring that data is saved whenever the form widget is removed from the widget tree (e.g., user navigates back).
  • A "Submit Form" button triggers _submitFormData, which simulates submission and then clears the temporary data.
  • An explicit "Save Form Temporarily" button is also provided for users who want to manually save progress without leaving the page.

Conclusion

shared_preferences offers a simple, efficient, and robust way to manage temporary form data in Flutter applications. By implementing this pattern, you can significantly enhance the user experience, preventing data loss and allowing users to seamlessly continue their input across sessions or navigation changes. While not a replacement for a full database solution, for lightweight, temporary data storage, it's an indispensable tool in any Flutter developer's arsenal.

Related Articles

May 14, 2026

Building a Multi-Event Countdown Timer Widget with Reminders, Notifications, Repeat, and Custom Labels in Flutter

Building a Multi-Event Countdown Timer Widget with Reminders, Notifications, Repeat, and Custom Labels in Flutter Countdown timers are essential in many applic

May 11, 2026

Unleashing Dynamic UIs: Flutter's Animation Prowess

Unleashing Dynamic UIs: Flutter's Animation Prowess for Slide & Scale Effects Flutter's declarative UI framework, combined with its powerful animation capabilit

May 11, 2026

Building a Product Detail Page Widget in Flutter with Related Items, Review Carousel, Promo Badges, and Quick Buy

Building a Product Detail Page Widget in Flutter with Related Items, Review Carousel, Promo Badges, and Quick Buy A well-designed Product Detail Page (PDP) is