How do I store persistent data in Flutter?

To store persistent data in Flutter we can adopt different approaches. Either we can use local storage in key, value pair array; or, we can use SQLite database.

In addition we can use remote database also.

However, the concept revolves around state management, accessorized with Provider package.

The question is, how we can use the state management using Provider package between screens and store persistent data in flutter.

One may ask, why should we use Provider package? We not stateful widget? After all, through stateful widget, we can also store persistent data in flutter.

In addition, stateful widget comes with Flutter, an in-built feature to maintain state.

The one and only answer is, through Provider package we can avoid widget rebuilds. To get the concept, and if you want an evidence please read my previous blog post on stateful vs stateless widgets.

Now, let us start learning how to store persistent data in flutter. At the very beginning, let me tell you, the full code snippet is available in the respective GitHub Repository.

We cannot use full code snippets. As they are too long. Therefore, we must use part of them for brevity.

Above all, we’ll follow MVC or Model-view-controller pattern.

So data comes from the models folder.

import 'package:flutter/foundation.dart';

class Book with ChangeNotifier {
  final String id;
  final String title;
  final String description;
  final double price;
  final String imageUrl;
  bool isFavorite;

  Book({
    required this.id,
    required this.title,
    required this.description,
    required this.price,
    required this.imageUrl,
    this.isFavorite = false,
  });

  void toggleFavoriteStatus() {
    isFavorite = !isFavorite;
    notifyListeners();
  }
}

In the above code, we have defined a Book class whose constructor would pass different properties including a boolean value isFavorite.

We’ve used Dart mixin to use ChangeNotifier, so that we can use ChangeNotifierProvider from provider package.

Now, we must have another data model which is actually our local storage. Based on the product ID, we can retrieve data from that storage.

import 'package:flutter/material.dart';

import 'book.dart';

class Books with ChangeNotifier {
  final List<Book> _items = [
    Book(
      id: 'p1',
      title: 'Beginning Flutter With Dart',
      description: 'You can learn Flutter as well Dart.',
      price: 9.99,
      imageUrl:
          'https://cdn.pixabay.com/photo/2014/09/05/18/32/old-books-436498_960_720.jpg',
    ),
    Book(
      id: 'p2',
      title: 'Flutter State Management',
      description: 'Everything you should know about Flutter State.',
      price: 9.99,
      imageUrl:
          'https://cdn.pixabay.com/photo/2016/09/10/17/18/book-1659717_960_720.jpg',
    ),
...
// code is incomplete for brevity

Now, the question is, how we will persist state in flutter?

How do you persist state in Flutter?

To persist state in Flutter, we must place our ChangeNotifierProvider on the top of the Book shopping cart that we’ve been building.

The following code snippet from the main dart file will give you an idea.

void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(
          create: (_) => Books(),
        ),
        ChangeNotifierProvider(
          create: (_) => Cart(),
        ),
        ChangeNotifierProvider(
          create: (_) => Orders(),
        ),
      ],
      child: const BookApp(),
    ),
  );
}

class BookApp extends StatelessWidget {
  const BookApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Book Shop',
      theme: ThemeData(
        primarySwatch: Colors.purple,
        primaryColor: Colors.deepOrange,
      ),
      home: const BooksOverviewScreen(),
      routes: {
        BookDetailScreen.routeName: (ctx) => const BookDetailScreen(),
        //CartScreen.routeName: (ctx) => CartScreen(),
      },
    );
  }
}

The home page route points to the books overview screen. However, with the help of other controllers we will be able to navigate to the book detail page, or screen.

The home screen will look like this:

Book App overview in Flutter
Book App overview in Flutter

Now, in our material design we’ve defined the route, so one click on the image will take us to the respective book detail screen.

Changed look of product detail page with the help of unique flutter font
Changed look of product detail page with the help of unique flutter font

In our controllers folder, we have two widgets that act as consumers who will listen to the notification sent by the ChangeNotifier.

The following code snippet will give us an idea.

class _BooksOverviewScreenState extends State<BooksOverviewScreen> {
  var _showOnlyFavorites = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Book Shop'),
        actions: <Widget>[
          PopupMenuButton(
            onSelected: (FilterOptions selectedValue) {
              setState(() {
                if (selectedValue == FilterOptions.Favorites) {
                  _showOnlyFavorites = true;
                } else {
                  _showOnlyFavorites = false;
                }
              });
            },
            icon: const Icon(
              Icons.more_vert,
            ),
            itemBuilder: (_) => [
              const PopupMenuItem(
                child: Text('Only Favorites'),
                value: FilterOptions.Favorites,
              ),
              const PopupMenuItem(
                child: Text('Show All'),
                value: FilterOptions.All,
              ),
            ],
          ),
        ],
      ),
      body: BooksGrid(showFavs: _showOnlyFavorites),
    );
  }
}

As we can see, the body of the books overview screen points to books grid widget. Therefore, we can have a look at that widget in controllers folder.

 @override
  Widget build(BuildContext context) {
    final productsData = Provider.of<Books>(context);
    final products = showFavs ? productsData.favoriteItems : productsData.items;
    return GridView.builder(
      padding: const EdgeInsets.all(10.0),
      itemCount: products.length,
      itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
        // builder: (c) => products[i],
        value: products[i],
        child: const BookItem(
            
            ),
      ),
..
// incomplete code for brevity

To persist data, we’ve used Provider.of method which passes context as parameter and uses the type Book class.

Here that plays the crucial role.

final productsData = Provider.of<Books>(context);

Not only that, the following part of the above code also helps us to persist data.

itemBuilder: (ctx, i) => ChangeNotifierProvider.value(
        // builder: (c) => products[i],
        value: products[i],
        child: const BookItem(
            
            ),
      ),

Although the Book App is in primary stage, still it’s worth taking a look at the GitHub Repository.

Moreover, we’ll keep building that e-commerce Book App in the future.

So, stay tuned and get in touch with all Flutter articles that always gives you updated information on Flutter.

What Next?

Books at Leanpub

Books in Apress

My books at Amazon

GitHub repository

Technical blog

Twitter

Comments

3 responses to “How do I store persistent data in Flutter?”

  1. […] already learned about persistent data, however, we have not discussed how we can pass data between […]

  2. […] There are many ways by which we can get data from model in flutter. Not only that, we can also store persistent data in flutter. […]

  3. […] As the name suggests, the Scoped Model in Flutter examines the scope and passes data downwards. We can create the scope at the top first with a type. And, after that in the descendant widgets, we can pass that data type. […]

Leave a Reply