Flutter and Supabase CRUD Operations Tutorial, Alternative for Firebase

Flutter Knowledge Sharing #63

Geno Tech
5 min readOct 2, 2022
Image: Flutter and Supabase Crud Operations Tutorial

Here we are discussing another important topic of our Flutter knowledge-sharing journey. In this article, you can go through flutter with a superbase. Supabase is a new open-source firebase alternative with a relational database management system called PostgreSQL, authentication storage, and serverless functions. The main difference is that Firebase is a document store, whereas Supabase is based on PostgreSQL — a relational, SQL-based database management system. Here we show how to perform CRUD operations with Supabase and Flutter. So If you are decided to integrate the superbase as your backend solution, this tutorial gives you a simple start within a few minutes. Try to build this simple Todo app by understanding how the Flutter app parses data with the Supabase project. It is easy. You can learn more about the difference with other backend solutions in Flutter.

Prerequisites

Before starting a new Flutter project, you need to have the flutter development environment and sign-up with Supabase and initiate a new free project. You can sign in to Supabase with your GitHub account.

Create a new Supabase project

Here I am going to create a simple to-do app for your clarification. Once you sign in to the Supabase you can create a new project by clicking new project. Give your project name and the password for the database. Then, clicking create a new project will create a new database. There you can create tables.

Now you can create a new table by clicking create a new table. Let’s create a table as ‘todotable’ and save it. Now you can add a new column called ‘task’ and text as the Data type. Add another column called ‘status’, select boolean as the Data type, and save it.

Create a new Flutter project

Now we are going to implement the Flutter code. So, create a Flutter project to show you this project’s functionalities. First, create a new Flutter application.

Setup Supabase plugin

First, add the supabase_flutter plugin to your project.

flutter pub add supabase_flutter
flutter pub get
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
supabase_flutter: ^1.2.2

Implemment the UI in main.dart

First, you need to import the supabase_flutter to the main.dart, and it needs to add the supaBaseURL and supaBaseKey. In your Supabase project, settings->API, you can find this URL and key.

Find your Supabase key and URL here

Here is the main.dart file.

import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:supabase_flutter_crud/supabase_handler.dart';

Future<void> main() async {
await Supabase.initialize(
url: 'Project URL',
anonKey: 'API Key');
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Supabase Flutter Demo',
home: MyWidget(),
);
}
}
class MyWidget extends StatefulWidget {
const MyWidget({Key? key}) : super(key: key);
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
User? _user;
late SupaBaseHandler supaBaseHandler = SupaBaseHandler();
String newValue = "";
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Supabase-Flutter CRUD Operations"),
),
body: FutureBuilder(
future: supaBaseHandler.readData(context),
builder: ((context, AsyncSnapshot snapshot) {
print("here ${snapshot.data.toString()}");
if (snapshot.hasData == null &&
snapshot.connectionState == ConnectionState.none) {}
print("here1 ${snapshot.data.toString()}");
return ListView.builder(
itemCount: snapshot.data?.length ?? 0,
itemBuilder: (context, index) {
return Container(
height: 150,
color:
snapshot.data![index]['status'] ? Colors.white : Colors.red,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
width: 200,
child: Center(
child: Text(snapshot.data![index]['task']),
),
),
IconButton(
icon: const Icon(Icons.done),
onPressed: () {
supaBaseHandler.updateData(
snapshot.data[index]['id'], true);
}),
IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
supaBaseHandler
.deleteData(snapshot.data[index]['id']);
setState(() {});
}),
],
),
);
},
);
}),
),
floatingActionButton: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
width: MediaQuery.of(context).size.width * 0.9,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: TextField(
onChanged: (value) {
print(value);
newValue = value;
},
),
),
FloatingActionButton(
onPressed: () {
supaBaseHandler.addData(newValue, false, context);
setState(() {});
},
child: const Icon(Icons.add),
),
FloatingActionButton(
onPressed: () {
setState(() {});
},
child: const Icon(Icons.refresh),
),
],
),
),
),
);
}
}

Create the SupaBaseHandler class

Now we are creating the Supabase handler class to handle the Add data, Read data, Update data, and Delete data functions.

Create/Add data

addData(String task, bool status, context) async {
try {
await Supabase.instance.client
.from('todotable')
.upsert({'task': task, 'status': status});
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('Saved the Task'),
));
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('Error saving task'),
backgroundColor: Colors.red,
));
}
}

Read Data

Future<List?> readData(context) async {
// var response = await client.from("todotable").select().order('task', ascending: true).execute();
print('Read Data');
try {
var response = await Supabase.instance.client.from('todotable').select();
print('Response Data ${response}');
final dataList = response as List;
return dataList;
} catch (e) {
print('Response Error ${e}');
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('Error occured while getting Data'),
backgroundColor: Colors.red,
));
return null;
}
}

Update Data

updateData(int id, bool statusval) async {
await Supabase.instance.client
.from('todotable')
.upsert({ 'id': id, 'status': statusval});
}

Delete Data

deleteData(int id) async {
await Supabase.instance.client
.from('todotable')
.delete()
.match({ 'id': id });
}

Finally, The whole application scenario is as follows. You can run and see the output on your emulator.

Issues Fixed

Here I faced the following issues additionally.

  1. PostgrestException(message: new row violates row-level security policy for table “profiles”, code: 42501, details: Forbidden, hint: null)

This error happened when adding data to the table because RLS enabled the table. You have to enable it, and the error will disappear. But make sure you will enable it when going to production.

Found this post useful? Kindly tap the 👏 button below! :)

--

--

Geno Tech

Software Development | Data Science | AI — We write rich & meaningful content on development, technology, digital transformation & life lessons.