Flutter SQLite FFI using moor
The purpose of this post is to Demo 'How to use SQLite FFI using moor library?'.
Flutter mobile can use the dart:ffi library to call native C APIs.FFI stands for foreign function interface. Other terms for similar functionality include native interface and language bindings.
In simple word, we do not need any method channel to call native apis of Android/IOS. We can directly access those in dart with help of ffi.
Let's get started:
Moor
Moor ffi
Step:1 Add Dependencies to pubspec
Add below dependencies in your pubspec.yaml.
moor: ^2.0.0
moor_ffi: ^0.0.1
provider: ^3.0.0+1
path_provider: ^1.3.0
dev_dependencies:
moor_generator: ^2.0.0
build_runner:
Step:2 create simple enitity class (movie_entity.dart)
import 'package:moor/moor.dart';
class MovieEntity extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get name => text().withLength(min: 1, max: 50)();
}
Step:3 create database class (app_db.dart)
import 'dart:io';
import 'package:moor/moor.dart';
import 'package:moor_ffi/moor_ffi.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p;
import 'movie_entity.dart';
part 'app_db.g.dart';
@UseMoor(tables: [MovieEntity])
class AppDatabase extends _$AppDatabase {
AppDatabase()
: super(
LazyDatabase(() async {
final dbFolder = await getApplicationDocumentsDirectory();
final file = File(p.join(dbFolder.path, 'myDB.sqlite'));
return VmDatabase(file);
}),
);
@override
int get schemaVersion => 1;
// This two methods can be extracted in DAO.
Future insertMovie(MovieEntityData movie) => into(movieEntity).insert(movie);
Stream<List<MovieEntityData>> watchAllMovies() => select(movieEntity).watch();
}
Let's Run the BuildRunner to generate code.
flutter pub run build_runner build
Once we generate the code, we will see MovieEntityData as DataClass and movieEntity as Table.
Step:4 Let's create UI (home_page.dart)
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:sql_ffi/app_db.dart';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final TextEditingController _controller = TextEditingController();
@override
Widget build(BuildContext context) {
final AppDatabase appDatabase = Provider.of<AppDatabase>(context);
return Scaffold(
appBar: AppBar(
title: Text('SQL FFI Demo'),
),
body: Column(
children: <Widget>[
StreamBuilder(
stream: appDatabase.watchAllMovies(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
List<MovieEntityData> users = snapshot.data;
if (users.isNotEmpty) {
return Expanded(
flex: 5,
child: ListView.separated(
padding: const EdgeInsets.all(16),
itemCount: users.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(users[index].name),
);
},
separatorBuilder: (BuildContext context, int index) =>
const Divider(),
),
);
}
}
return const SizedBox(height: 10);
},
),
const SizedBox(height: 20),
Container(
padding: const EdgeInsets.all(10.0),
color: Colors.grey[200],
child: Align(
alignment: Alignment.bottomCenter,
child: Column(
children: <Widget>[
TextField(
decoration: InputDecoration(
hintText: 'Enter movie name',
),
controller: _controller,
),
RaisedButton(
child: const Text('Save Movie'),
onPressed: () async {
final movieName = _controller.text;
if (movieName.isNotEmpty) {
int id = Random().nextInt(100);
MovieEntityData movieEntity =
MovieEntityData(id: id, name: '$movieName');
await appDatabase.insertMovie(movieEntity);
_controller.clear();
}
},
),
],
),
),
),
const SizedBox(height: 20),
],
),
);
}
}
Step:5 Let's create UI (main.dart)
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'app_db.dart';
import 'home_page.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Provider(
builder: (_) => AppDatabase(),
child: MaterialApp(
title: 'Material App',
home: HomePage(),
),
);
}
}
That's it enjoy