Newby Coder header banner

Flutter Json Storage

Persisting Flutter Android/iOS app state across restarts

States are typically stored in local storage so that a user's activity on an application can appear to be persistent

state_persistence package enables to store data as a local JSON file called data.json in the applications data directory

It can also be integrated with some other type of storage like Firebase


Creating new Flutter App

Check Flutter installation to setup Flutter

Use flutter create command to create a Flutter project (here local_storage_app :

flutter create local_storage_app

Dependency

Implementation

Importing Package
import 'package:state_persistence/state_persistence.dart';

Declare PersistedAppState

In entry widget, wrap MaterialApp as child of PersistedAppState

Assign storage property to JsonFileStorage with optional initialData

storage can also be instance of some other storage class which extends PersistedStateStorage

class PersistentStorageApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return PersistedAppState(
      storage: JsonFileStorage(initialData: {
        'tab': 1,
      }),
      child: MaterialApp(
        title: 'Persistent Storage Example App',
        theme: ThemeData(primarySwatch: Colors.blue),
        home: EgWidget(),
      ),
    );
  }
}

Get/set data using PersistedData object

Use PersistedAppState.of() method to get PersistedData instance for context

@override
  void didChangeDependencies() {
    super.didChangeDependencies();
    PersistedData _data = PersistedAppState.of(context);
    // get value for key 'tab'
    var _initialIndex = _data['tab']
  }

// set value
_data['tab'] = 0

Data that is set to PersistedData is saved to disk based on the saveTimeout given to the PersistedAppState widget whose default value is 500 milliseconds

PersistedStateBuilder

Example of builder :

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

void main() => runApp(App());

class App extends StatefulWidget {
  @override
  _AppState createState() => _AppState();
}

class _AppState extends State<App> {
  TextEditingController _textController;

  @override
  Widget build(BuildContext context) {
    return PersistedAppState(
      storage: JsonFileStorage(),
      child: MaterialApp(
        title: 'Persistent TextField Example',
        theme: ThemeData(primarySwatch: Colors.indigo),
        home: Scaffold(
          appBar: AppBar(title: Text('Persistent TextField Example')),
          body: Container(
            padding: const EdgeInsets.all(32.0),
            alignment: Alignment.center,
            child: PersistedStateBuilder(
              builder: (BuildContext context, AsyncSnapshot<PersistedData> snapshot) {
                if (snapshot.hasData) {
                  if (_textController == null) {
                    _textController = TextEditingController(text: snapshot.data['text'] ?? '');
                  }
                  return TextField(
                    controller: _textController,
                    decoration: InputDecoration(
                      hintText: 'Enter some text',
                    ),
                    onChanged: (String value) => snapshot.data['text'] = value,
                  );
                } else {
                  return CircularProgressIndicator();
                }
              },
            ),
          ),
        ),
      ),
    );
  }
}

App Code

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:state_persistence/state_persistence.dart';


void main() => runApp(PersistentStorageApp());

class PersistentStorageApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return PersistedAppState(
      storage: JsonFileStorage(initialData: {
        'tab': 1,
      }),
      child: MaterialApp(
        title: 'Persistent Storage Example App',
        theme: ThemeData(primarySwatch: Colors.blue),
        home: EgWidget(),
      ),
    );
  }
}

class EgWidget extends StatefulWidget {
  @override
  _EgWidgetState createState() => _EgWidgetState();
}

class _EgWidgetState extends State<EgWidget> with SingleTickerProviderStateMixin {
  PersistedData _data;
  TabController _controller;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _data = PersistedAppState.of(context);
    if (_data != null && _controller == null) {
      _controller = TabController(initialIndex: _data['tab'] ?? 0, vsync: this, length: 4);
      _controller.addListener(_onTabChanged);
    }
  }

  void _onTabChanged() {
    if (!_controller.indexIsChanging) {
      _data['tab'] = _controller.index;
    }
  }

  @override
  void dispose() {
    _controller?.removeListener(_onTabChanged);
    _controller?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (_data != null) {
      return Scaffold(
        appBar: AppBar(
          title: Text('Persistent Tab Example'),
          bottom: TabBar(
            controller: _controller,
            tabs: [
              Tab(text: 'Tab 1'),
              Tab(text: 'Shared Preference'),
              Tab(text: 'Json Storage'),
              Tab(text: 'Cache Storage'),
            ],
          ),
        ),
        body: TabBarView(
          controller: _controller,
          children: [
            Container(color: Colors.cyanAccent[400], child: Center(child: Text('Tab 1', style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white)))),
            Container(color: Colors.amber[600], child: Center(child: Text('Tab 2', style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white)))),
            Container(color: Colors.greenAccent[700], child: Center(child: Text('Tab 3', style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white)))),
            Container(color: Colors.brown[900], child: Center(child: Text('Tab 1', style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white)))),
          ],
        )
      );
    }
    else {
      return Center(child: CircularProgressIndicator());
    }
  }
}

Run instructions

Ensure a supported device is connected or emulator/simulator is started

Go to project directory

Use flutter run command to run

flutter run

It builds and runs app on an available android/ios device


Screenshot/image

Android

cl-flutter-state-persistence

iOS

cm_flutter_state_persistence_as1cm_flutter_state_persistence_as2