7

I am developing a barcode app and save the data to hive. What I need to know is there a way to export the saved hive database to a backup file and be able to retrieve it for instance if the app crashed or your phone is lost. This is for blind accessibility. Want to export the data to a file that I can save to my pc to store and if something happens I do not have to scan all the products again to build the database. If hive can not do this can someone point me in a direction of which flutter dart database can do this. Thank you

Ok the answer did not work for me. Here is a copy of my model file

    import 'package:hive/hive.dart';

    part 'product.g.dart';
    @HiveType(typeId: 0)
    class Product extends HiveObject{
      @HiveField(0)
      String itemName;
      @HiveField(1)
      String barCode;
      @HiveField(2)
      String bcType;

      Product(this.itemName, this.barCode, this.bcType);
    }

Then I call my box like var box = Hive.box('products');

How to encode this to json for saving?

I use the next

    Future<File> _createBackupFile() async {
      /// This example uses the OS temp directory

File backupFile = File('${Directory.systemTemp.path}/backup_barcode.json');

      try {
        /// barcodeBox is the [Box] object from the Hive package, usually exposed inside a [ValueListenableBuilder] or via [Hive.box()]
    var barcodeBox = Hive.box<Product>('products');
       backupFile = await backupFile.writeAsString(jsonEncode(barcodeBox.values));

        return backupFile;
      } catch (e) {
        // TODO: handle exception
    print(e);
      }
    }
Hendrik
  • 135
  • 1
  • 10

2 Answers2

4

There is not a "out-of-the-box" solution for that as far as I know. It depends a lot on your use case of how you want to do that (since there are many ways). For a complete example of how I did that for my app, you can take a look here: https://github.com/Kounex/obs_blade/blob/master/lib/views/settings/logs/log_detail/log_detail.dart (I made use of the share package in order to easily export it - but that's not necessary)

Flutter also has its own documentation on reading and writing files (https://flutter.dev/docs/cookbook/persistence/reading-writing-files) - I will add some information to round it up:

Storage location


First of all we have to think about where to store the "backup file". Flutter exposes common paths on its own which you can make use of (additionally the path_provider package gives you more flexibility). If you want this backup file to be temporarily, you can for example use:

Directory.systemTemp;

The documentation states: "This is the directory provided by the operating system for creating temporary files and directories in." The OS will make sure to delete them in different occasions so you don't have to worry about it. You can also create additional directories inside this temp directory to make it more distinguishable, like:

Directory.systemTemp.createTemp('my_app');

IMPORTANT: this applies to non-sensitive data. If whatever you are processing contains sensitive data (like names, addresses etc.), you have to ensure data security / data privacy. In such cases I would make use of the path_provider package as mentioned earlier and create those files in the documents directory (getApplicationDocumentsDirectory()) and make sure they are deleted immediately after usage / export. Even encrypting the content may be a good idea - but I'm not diving into this here.

File mangagement


Once we know where to store the file, we just need to create them. Chapter 3 and 4 of the flutter documentation earlier exactly states how to do that, so I'm rather focusing on what to write.

A common and very convenient way to compose your data is JSON. Flutter also has documentation for that: https://flutter.dev/docs/development/data-and-backend/json

Since you are using Hive, you probably already have classes representing entries in your boxes and you could easily just add the toJson() function where you return a Map<String, dynamic> (as seen in the documentation) and you can use that to finally write the needed information into a file.

Based on your Hive class, this is how to adjust it in otder to serialize it correctly:

import 'package:hive/hive.dart';

part 'product.g.dart';

@HiveType(typeId: 0)
class Product extends HiveObject{
  @HiveField(0)
  String itemName;

  @HiveField(1)
  String barCode;

  @HiveField(2)
  String bcType;

  Product(this.itemName, this.barCode, this.bcType);

  /// This function will automatically be used by the [jsonEncode()] function internally
  Map<String, dynamic> toJson() => {
    'itemName': this.itemName,
    'barCode': this.barCode,
    'bcType': this.bcType,
  }
}

A small example implementation could look like this:

Future<File?> _createBackupFile() async {
  /// This example uses the OS temp directory
  File backupFile = File('${Directory.systemTemp.path}/backup_barcode.json');

  try {
    /// barcodeBox is the [Box] object from the Hive package, usually exposed inside a [ValueListenableBuilder] or via [Hive.box()]
    backupFile = await backupFile.writeAsString(jsonEncode(barcodeBox.values));

    return backupFile;
  } catch (e) {
    // TODO: handle exception
  }
}

This will save the JSON representation of your Hive box inside the temporary OS directory. You can swap the directory with whatever suits you best (on Android for example on the external storage for easier accessibility).

Now you have to think about how and when to trigger this. You can do this manually by triggering a button press for example or automatically after a certain action (like adding a new barcode) and choose a way that works for you to access the file. As stated earlier, saving the file somewhere easily accessible like the external storage on Android or making use of the share package are possible solutions.

kounex
  • 1,555
  • 4
  • 12
  • I will give this a go. Thank you very much for your detailed explanation. Thank you – Hendrik Jun 07 '21 at 10:09
  • Just one other thing. How do I import the saved file? – Hendrik Jun 07 '21 at 10:14
  • What do you mean by import the saved file? Like get access to the newly created file? That's what I meant by storing it somewhere accessible like the external storage or use the share package to send it via mail or whatever! – kounex Jun 07 '21 at 11:45
  • Throws this error. But will spend more time tonight to see if I can try and figure it out. Doing it on the fly at the oment. – Hendrik Jun 07 '21 at 12:46
  • I edited my answer to add the necessary `toJson()` function in order to work - you can read it up in the documentation i provided – kounex Jun 08 '21 at 10:25
  • 2
    Gives Converting object to an encodable object failed: Instance of 'MappedIterable', not sure what I am doing wrong. Thanks – Hendrik Jun 09 '21 at 03:34
  • Get this Converting object to an encodable object failed: Instance of 'MappedIterable'. Not sure what to do – Hendrik Jun 09 '21 at 18:37
1

Android Manifest should contain these:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<application
        android:requestLegacyExternalStorage="true"

You will need this package and this package to proceed.

Now a method to backup the data to a desired location:

Future<void> createBackup() async {
if (Hive.box<Product>('products').isEmpty) {
  ScaffoldMessenger.of(context).showSnackBar(
    const SnackBar(content: Text('No Products Stored.')),
  );
  return;
}
ScaffoldMessenger.of(context).showSnackBar(
  const SnackBar(content: Text('Creating backup...')),
);
Map<String, dynamic> map = Hive.box<Product>('products')
    .toMap()
    .map((key, value) => MapEntry(key.toString(), value));
String json = jsonEncode(map);
await Permission.storage.request();
Directory dir = await _getDirectory();
String formattedDate = DateTime.now()
    .toString()
    .replaceAll('.', '-')
    .replaceAll(' ', '-')
    .replaceAll(':', '-');
String path = '${dir.path}$formattedDate.json';//Change .json to your desired file format(like .barbackup or .hive).
File backupFile = File(path);
await backupFile.writeAsString(json);
ScaffoldMessenger.of(context).showSnackBar(
  const SnackBar(content: Text('Backup saved in folder Barcodes')),
);}


Future<Directory> _getDirectory() async {
    const String pathExt = 'Barcodes/';//This is the name of the folder where the backup is stored
    Directory newDirectory = Directory('/storage/emulated/0/' + pathExt);//Change this to any desired location where the folder will be created
    if (await newDirectory.exists() == false) {
      return newDirectory.create(recursive: true);
    }
    return newDirectory;
  }

Finally, call this function using a button and it will save a backup with the current time as the name in JSON format.

createBackup()

After this to restore the data back to Hive,

Future<void> restoreBackup() async {
ScaffoldMessenger.of(context).showSnackBar(
  const SnackBar(content: Text('Restoring backup...')),
);
FilePickerResult? file = await FilePicker.platform.pickFiles(
  type: FileType.any,
);
if (file != null) {
  File files = File(file.files.single.path.toString());
  Hive.box<Product>('products').clear();
  Map<String, dynamic> map = jsonDecode(await files.readAsString());
  for (var i = 0; i < map.length; i++) {
    Product product = Product.fromJson(i.toString(), map);
    Hive.box<Product>('products').add(product);
  }
  ScaffoldMessenger.of(context).showSnackBar(
    const SnackBar(content: Text('Restored Successfully...')),
  );
}
}

Finally, call this function using a button and it will open the file picker where you can select the backup file and it will remove the existing data and add every item from the backup in a loop.

restoreBackup()
  • Hi. Please, if you can, give listening of Product.fromJson(i.toString(), map) function – Tas Jan 31 '22 at 16:14