How to save to local storage (shared preferences) in Flutter

Share this post

To save to local storage or Shared Preferences (as it’s called in Android) in Flutter, you can use the shared_preferences plugin. First of all, you have to add the dependency to your pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter
  shared_preferences: "<newest version>"

Writing data to local storage in Flutter

You can write data to local storage is easy. We just need to get an instance of our SharedPreferences object and call our set function depending on the type of our data. You could write your save function as follows:

// we have an async function because getInstance() return a Future
_saveData() async {
    // we make sure we have an instance
    final prefs = await SharedPreferences.getInstance();
    prefs.setInt('yourKey', 100);
  }

In addition to int you can save the following types of data:

  • bool: setBool()
  • double: setDouble()
  • String: setString()
  • List<String>: setStringList()

Shared Preferences is intended for storing a relatively small amount of data and is usually suitable for saving user’s preferences. For example, if a user wants to use your app in dark mode, that setting should be saved somewhere and this is a perfect use case for Shared Preferences.

Reading local storage in Flutter

To read the data that we have saved previously, we can do the following:

// our return value is Future<int> because we have to "await" an instance
Future<int> _getData() async {
    final prefs = await SharedPreferences.getInstance();
    // we make sure we have some value to set it to 0 if there is no value
    return prefs.getInt('yourKey') ?? 0;
  }

Obviously, there are get methods for other types as well, just try it out yourself.

Removing data from local storage in Flutter

To remove data we can simply call remove providing our key:

_removeData() async {
    final prefs = await SharedPreferences.getInstance();
    prefs.remove('yourKey');
  }

How to use progress indicators in Flutter

Share this post

There are 2 dedicated widgets in Flutter that can help you display progress in your Flutter apps, namely LinearProgressIndicator and CircularProgressIndicator. You can use both of them just the way you use any other widget and you can position them however you want.

Progress indicators in Flutter

How to use LinearProgressIndicator in Flutter

A LinearProgressIndicator is basically a progress bar that shows the progress in a line. By default, all properties are optional, which means that the indicator will be an indeterminate progress bar. That means that we don’t really know when the process will be finished, as opposed to a determinate LinearProgressIndicator of which the value property should be updated.

The following code shows a simple usage of (indeterminate) LinearProgressIndicator that simulates a download (but we don’t know when it will be finished). A button simply changes the state from not downloading to downloading. When we are not downloading, we show a text instructing the user and otherwise we show the progress indicator:

bool _loading;

  @override
  void initState() {
    super.initState();
    _loading = false;
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("LinearProgressIndicator"),
      ),
      body: Center(
        child: Container(
          padding: EdgeInsets.all(16.0),
          child: _loading ? LinearProgressIndicator() : Text("Press button to download"),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            _loading = !_loading;
          });
        },
        tooltip: 'Download',
        child: Icon(Icons.cloud_download),
      ),
    );
  }

This is how it will look:

What if you want to show make a determinate progress indicator, that means you want to show how much we have progressed or how long it will take more to finish downloading something. We can simulate a download that takes a certain amount of time and updates the value of LinearProgressIndicator as follows:

  bool _loading;
  double _progressValue;

  @override
  void initState() {
    super.initState();
    _loading = false;
    _progressValue = 0.0;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("LinearProgressIndicator"),
      ),
      body: Center(
        child: Container(
          padding: EdgeInsets.all(16.0),
          child: _loading
              ? Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    LinearProgressIndicator(
                      value: _progressValue,
                    ),
                    Text('${(_progressValue * 100).round()}%'),
                  ],
                )
              : Text("Press button to download"),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            _loading = !_loading;
            _updateProgress();
          });
        },
        tooltip: 'Download',
        child: Icon(Icons.cloud_download),
      ),
    );
  }

  // we use this function to simulate a download
  // by updating the progress value
  void _updateProgress() {
    const oneSec = const Duration(seconds: 1);
    new Timer.periodic(oneSec, (Timer t) {
      setState(() {
        _progressValue += 0.2;
        // we "finish" downloading here
        if (_progressValue.toStringAsFixed(1) == '1.0') {
          _loading = false;
          t.cancel();
          _progressValue: 0.0;
          return;
        }
      });
    });
  }

And it will look like this:

How to use CircularProgressIndicator in Flutter

CircularProgressIndicator works exactly the same way as LinearProgressIndicator, so we can just replace that first one with the latter:

  bool _loading;
  double _progressValue;

  @override
  void initState() {
    super.initState();
    _loading = false;
    _progressValue = 0.0;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("LinearProgressIndicator"),
      ),
      body: Center(
        child: Container(
          padding: EdgeInsets.all(16.0),
          child: _loading
              ? Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    CircularProgressIndicator(
                      value: _progressValue,
                    ),
                    Text('${(_progressValue * 100).round()}%'),
                  ],
                )
              : Text("Press button to download"),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            _loading = !_loading;
            _updateProgress();
          });
        },
        tooltip: 'Download',
        child: Icon(Icons.cloud_download),
      ),
    );
  }

  // we use this function to simulate a download
  // by updating the progress value
  void _updateProgress() {
    const oneSec = const Duration(seconds: 1);
    new Timer.periodic(oneSec, (Timer t) {
      setState(() {
        _progressValue += 0.2;
        // we "finish" downloading here
        if (_progressValue.toStringAsFixed(1) == '1.0') {
          _loading = false;
          t.cancel();
          _progressValue: 0.0;
          return;
        }
      });
    });
  }

Which looks like this:

If you don’t care about how much progress is left and want to have an indeterminate one, just remove the value property and the _updateProgress function. Have fun developing with Flutter.

How to use Row and Column in Flutter with examples

Share this post

Row and Column might be the most important (multi-child) layout widgets in Flutter. Row and Column let you align child widgets horizontally and vertically. If you are a web developer, you can compare them with a div with the following properties in CSS:

.row {
 display: flex;
 flex-direction: row; // this is the default
}

.column {
 display: flex;
 flex-direction: column;
}

Use case: How to create a list item using Row widget in Flutter

I’m going to show you how to create a nice list item using a Row widget. This is a good example because in a list item the information is aligned horizontally. Let’s say we want to show a list of contacts like we have in WhatsApp and we want to define how each of these list items will look like. Let’s say we have the following requirements:

  • An Avatar
  • The name of our contact
  • List item must be clickable with some visual feedback
  • The widgets inside Row must be aligned nicely

First, we need an avatar for our contact. Flutter provides us with a very nice widget called CircleAvatar that gives you a ready-to-use circular widget specially made for this purpose. So we will use that one. We define a background color for our avatar and take the first letter of our contact instead of an image. For the name of our contact, we will be using the Text widget obviously.

Our widget structure will look like this:

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Row(
        children: <Widget>[
          CircleAvatar(
            backgroundColor: Colors.blue,
            child: Text(
              'J',
              style: TextStyle(
                fontWeight: FontWeight.bold,
                color: Colors.white,
              ),
            ),
          ),
          Text('John Doe'),
        ],
      ),
    );
  }

And will look like this on our device:

Alignment using Row widget

It does not look very great, but it’s a good start. First of all, our Row is too close to the left and the top. Somehow we need to add distance between the Row and the parent widget. We will use a Container for that with small padding.

The next thing we probably want to fix is the fact that the text and the avatar are too close to each other. We will put a SizedBox between them with the desired width.

Lastly, we wrap our Row inside an InkWell. InkWell provides us with GestureDetector (which gives us an onTap callback) plus visual feedback.

The improved code:

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: InkWell(
        child: Container(
          padding: EdgeInsets.all(16.0),
          child: Row(
            children: <Widget>[
              CircleAvatar(
                backgroundColor: Colors.blue,
                child: Text(
                  'J',
                  style: TextStyle(
                    fontWeight: FontWeight.bold,
                    color: Colors.white,
                  ),
                ),
              ),
              SizedBox(
                width: 16.0,
              ),
              Text('John Doe'),
            ],
          ),
        ),
        onTap: () {
          // do something here
        },
      ),
    );
  }

And will look like this on our device:

Alignment using Row widget

It looks much better now and it’s tappable. Of course, there are many more use cases where you would be using the Row widget, but the principle stays the same.

Alignment properties in Row and Column

Row and Column have the same alignment properties, they work for the mainAxisAlignment (horizontal in Row and vertical in Column) and crossAxisAlignment (the opposite of mainAxis). For example, if I want to define that child widgets should be aligned vertically within the Row widget, I would have to use the crossAxisAlignment, as I’m defining the alignment in the opposite direction. These 2 alignment categories can be set to one of the following values:

  • start
  • center
  • end
  • spaceAround: Place the free space evenly between the children as well as half of that space before and after the first and last child.
  • spaceBetween: Place the free space evenly between the children.
  • spaceEvenly: Place the free space evenly between the children as well as before and after the first and last child.

Let’s say we decide to use spaceBetween to align our avatar and text in the previous example. It will then look like this:

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: InkWell(
        child: Container(
          padding: EdgeInsets.all(16.0),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: <Widget>[
              CircleAvatar(
                backgroundColor: Colors.blue,
                child: Text(
                  'J',
                  style: TextStyle(
                    fontWeight: FontWeight.bold,
                    color: Colors.white,
                  ),
                ),
              ),
              SizedBox(
                width: 16.0,
              ),
              Text('John Doe'),
            ],
          ),
        ),
        onTap: () {
          // do something here
        },
      ),
    );
  }

Which will look like this:

MainAxisAlignment.spaceBetween

You can try the other properties yourself to see what happens (if you are a web developer, it works the same as flex properties.

Use case: How to create a login form using Column in Flutter

A very good example of when Column comes in handy is probably alignment of form widgets in a page. That’s because we want to align the widgets vertically aka in a Column.

Let’s create a login form containing a username and a password field. I’m not going too deep into technicalities, as I’m focusing on alignment for now. I will write a separate post about how to properly use a form in Flutter.

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Container(
        padding: EdgeInsets.all(16.0),
        child: Column(
          children: <Widget>[
            TextField(
              keyboardType: TextInputType.emailAddress,
              decoration: InputDecoration(
                hintText: "Your email address",
              ),
            ),
            SizedBox(
              height: 32,
            ),
            TextField(
              keyboardType: TextInputType.text,
              obscureText: true, // because it's a password
              decoration: InputDecoration(
                hintText: "Your password",
              ),
            ),
            SizedBox(
              height: 32,
            ),
            RaisedButton(
              child: Text("Login"),
              color: Colors.blue,
              textColor: Colors.white,
              onPressed: () {
                // perform login
              },
            ),
          ],
        ),
      ),
    );
  }

This will look like this:

This post explains the basic working of Row and Column which align their children in a horizontal and vertical way. There are more advanced alignments within Row and Column, like how much each child should occupy, which I will explain in a separate post.