sqliteflutterdartsqflite

Insert sqlite flutter without freezing the interface


I'm trying to insert a lot of rows (about 12k or more) in a sqlite memory database using flutter.

I get data from the API and use a compute function in order to process data from Json. Now I need to add these data to a database in memory, in order to do so I use a transaction with a batch.

batchInsertEventSong(List<EventSong> rows) async {
   Database db = await instance.database;
   db.transaction((txn) async {
      Batch batch = txn.batch();
      for (var song in rows) {
         Map<String, dynamic> row = {
           DatabaseHelper.columnIdSong: song.id,
           DatabaseHelper.columnTitle: song.title,
           DatabaseHelper.columnInterpreter: song.interpreter
         };
         batch.insert(table, row);
       }
      batch.commit();
   }
}

But this function is blocking my UI during insertions, I tried also with compute but I can't pass the class db or the class batch. I hadn't clear how to execute this process in another thread or (since I can't use isolates) executing without blocking my UI.

Any advice?


Solution

  • Update 2020 - 05 - 15

    this will not work see :


    Note : full code available on the end

    gif of the working sampe app

    Step 1 : make your method static and make it void

      static batchInsertEventSong(List<EventSong> rows) {
        Database db = await instance.database;
        db.transaction((txn) async {
            Batch batch = txn.batch();
            for (var song in rows) {
                Map<String, dynamic> row = {
                DatabaseHelper.columnIdSong: song.id,
                DatabaseHelper.columnTitle: song.title,
                DatabaseHelper.columnInterpreter: song.interpreter
                };
                batch.insert(table, row);
            }
            batch.commit();
        }
      }
    

    Step 2 : create new method (usually but not required add Async for the same name)

      Future batchInsertEventSongAsync(List<EventSong> rows) {
    
      }
    

    Step 3 : call compute with your method static and return

      return compute(batchInsertEventSong, rows);
    

    Step [1,2,3] code review

      Future batchInsertEventSongAsync(List<EventSong> rows) {
        return compute(_batchInsertEventSong, rows);
      }
    
      static _batchInsertEventSong(List<EventSong> rows) {
        Database db = await instance.database;
        db.transaction((txn) async {
            Batch batch = txn.batch();
            for (var song in rows) {
                Map<String, dynamic> row = {
                DatabaseHelper.columnIdSong: song.id,
                DatabaseHelper.columnTitle: song.title,
                DatabaseHelper.columnInterpreter: song.interpreter
                };
                batch.insert(table, row);
            }
            batch.commit();
        }
      }
    

    Full Code

    import 'dart:async';
    import 'package:flutter/foundation.dart';
    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: Scaffold(body: MyHomePage(title: 'Flutter Demo Home Page')),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                FlatButton.icon(
                  icon: Icon(Icons.backup),
                  label: Text("Long Opreation"),
                  onPressed: () async {
                    var rows = await RsetApi.getRawsAsync();
                    await Database._saveRaws(rows);
                  },
                ),
                FlatButton.icon(
                  icon: Icon(Icons.backup),
                  label: Text("Short Opreation"),
                  onPressed: () {
                    Scaffold.of(context).hideCurrentSnackBar();
                    Scaffold.of(context).showSnackBar(new SnackBar(
                      content: new Text(DateTime.now().toIso8601String()),
                    ));
                  },
                ),
              ],
            ),
          ),
        );
      }
    }
    
    class RsetApi {
      static Future<List<EventSong>> getRawsAsync() {
        return compute(_getRaws, null);
      }
    
      static List<EventSong> _getRaws(pram1) {
        var rows = List<EventSong>();
        for (var i = 1; i < 12000; i++) {
          rows.add(EventSong(i));
          print("fetching raws " + (i / 12000).toString());
        }
        return rows;
      }
    }
    
    class Database {
      static Future saveRawsAsync(List<EventSong> rows) {
        return compute(_saveRaws, rows);
      }
    
      static _saveRaws(List<EventSong> rows) {
        for (var i = 1; i < rows.length; i++) {
          print("saving raws " + (i / rows.length).toString());
        }
      }
    }
    
    class EventSong {
      int id;
      EventSong(this.id);
    }
    

    Ref:

    non-important Ref: