Newby Coder header banner

Flutter Json Storage

Json Storage in Flutter Android/iOS application

An application might require local storage to store information about user's activity and/or profile

localstorage package provides LocalStorage which can be used to implement json storage in an application


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:localstorage/localstorage.dart';

Create instance of LocalStorage

A string is passed to LocalStorage as an identifier

  final LocalStorage storage = new LocalStorage('local_storage_example_widget');

Check if storage is ready

storage.ready.then((ready) {
    setState(() {
      var item = storage.getItem('local_storage_example_widget');
    });
});

Get data from storage

Get storage data for a specific key

var item = storage.getItem('local_storage_example_widget');

Set data

Store an object for a specific key

storage.setItem('local_storage_example_widget', map);

App Code

The home screen widget renders a TabBar whose selected index is stored to persist over restarts (check app with state persistence)

JsonStorageWidget renders an input form and uses LocalStorage to store username and email, when user types in those fields, and a boolean for a checkbox field

password is stored when submit button is clicked and these are filled when app is restarted

If the boolean value is true then user is shown some data instead of form when submit button is clicked or app is restarted (by using another bool)

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:state_persistence/state_persistence.dart';
import 'package:localstorage/localstorage.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) {
      print("init controller");
      _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)))),
            JsonStorageWidget(),
            Container(color: Colors.brown[900], child: Center(child: Text('Tab 1', style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white)))),
          ],
        )
      );
    }
    else {
      return Center(child: CircularProgressIndicator());
    }
  }
}

class UserData {
  String email = '';
  String password = '';
  String username = '';
  bool isIt = true;

  toJSONEncodable() {
    Map<String, dynamic> map = new Map();
    map['email'] = email;
    map['password'] = password;
    map['username'] = username;
    map['isIt'] = isIt;
    return map;
  }

  static UserData from(Map<String, dynamic> map) {
    UserData userData = new UserData();
    userData.username = map['username'] ;
    userData.email = map['email'] ;
    userData.password = map['password'] ;
    userData.isIt = map['isIt'] ;
    return userData;
  }
}

Map<String, TextEditingController> getTextEditingControllers(List<String> labels) {
  Map<String, TextEditingController> map = new Map();
  for(String label in labels) {
    map[label] = new TextEditingController();
  }
  return map;
}

class JsonStorageWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => new JsonStorageWidgetState();
}

class JsonStorageWidgetState extends State<JsonStorageWidget> {
  final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
  UserData userData = new UserData();
  final Map<String, TextEditingController> tecMap =
    getTextEditingControllers(['email','password','username','isIt']);
  final LocalStorage storage = new LocalStorage('local_storage_example_widget');
  bool showForm = true;

  void initState() {
    super.initState();
    init();
  }

  void init() async {
    storage.ready.then((ready) {
      if(mounted)
        setState(() {
          _getFromStorage();
          if(showForm) {
            _setFormValues();
          }
        });
    });
  }

  void submit() {
    if (this._formKey.currentState.validate()) {
      _formKey.currentState.save();
      setState(() {
        if(userData.isIt) {
          showForm = false;
        }
        _saveToStorage();
        _getFromStorage();
        _setFormValues();
      });
    }
  }

  _getFromStorage() {
    userData = new UserData();
    var item = storage.getItem('local_storage_example_widget');
    if(item != null) {
      userData = UserData.from(item);
      showForm = item["showForm"];
    }
  }

  _setFormValues() {
    _setControllerValue(tecMap['email'], userData.email);
    _setControllerValue(tecMap['password'], userData.password);
    _setControllerValue(tecMap['username'], userData.username);
  }

  _setControllerValue(TextEditingController controller, String value) {
    controller.value = TextEditingValue(
      text: value,
      selection: TextSelection.fromPosition(
        TextPosition(offset: value.length),
      ),
    );
  }

  _saveToStorage() {
    var map = userData.toJSONEncodable();
    map["showForm"] = showForm;
    print(map);
    storage.setItem('local_storage_example_widget', map);
  }

  _clearStorage() async {
    await storage.clear();
    setState(() {
      showForm = true;
      _getFromStorage();
      _setFormValues();
    });
  }

  Widget getForm() {
    return new Form(
      key: this._formKey,
      child: new ListView(
        children: <Widget>[
          new TextFormField(
            decoration: new InputDecoration(hintText: 'ign', labelText: 'Username'),
            controller: tecMap["username"],
            onChanged: (String value) { this.userData.username = value; _saveToStorage(); }
          ),
          new TextFormField(
            keyboardType: TextInputType.emailAddress,
            decoration: new InputDecoration(hintText: 'Email', labelText: 'Enter email'),
            onChanged: (String value) { this.userData.email = value; _saveToStorage(); },
            controller: tecMap["email"],
          ),
          new TextFormField(
            obscureText: true, // To display typed char with *
            decoration: new InputDecoration(hintText: 'Password',
              labelText: 'Enter password'
            ),
            onSaved: (String value) { this.userData.password = value; _saveToStorage(); },
            controller: tecMap["password"],
          ),
          CheckboxListTile(
            value: this.userData.isIt,
            title: Text("Is it?"),
            onChanged: (bool selected) {
              setState(() {
                this.userData.isIt = selected;
              });
            },
          ),
          new Container(
            child: new RaisedButton(
              child: new Text('Login', style: new TextStyle(color: Colors.white),),
              onPressed: this.submit,
              color: Colors.blue,
            ),
            margin: new EdgeInsets.only(top: 20.0),
          ),
        ],
      ),
    );
  }

  Widget showData() {
    return new ListView(
      children: <Widget>[
        Text("${this.userData.username}"),
        Text("${this.userData.email}"),
        Text("${this.userData.password}"),
        new Container(
          child: new RaisedButton(
            child: new Text('Clear Storage', style: new TextStyle(color: Colors.white),),
            onPressed: this._clearStorage,
            color: Colors.blue,
          ),
          margin: new EdgeInsets.only(top: 20.0),
        ),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    final Size screenSize = MediaQuery.of(context).size;
    return new Scaffold(
      appBar: new AppBar(title: new Text('JsonStorageWidget'),),
      body: new Container(
        padding: new EdgeInsets.all(20.0),
        child: showForm? getForm(): showData(),
      ),
    );
  }
}

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-local-storage-json

iOS

cm_flutter_local_storage_as1cm_flutter_local_storage_as2cm_flutter_local_storage_as3
cm_flutter_local_storage_as4cm_flutter_local_storage_as5