supabase_flutter 2.17.1
supabase_flutter: ^2.17.1 copied to clipboard
Flutter integration for Supabase. This package makes it simple for developers to build secure and scalable products.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
Future<void> main() async {
await Supabase.initialize(
url: 'SUPABASE_URL',
publishableKey: 'SUPABASE_PUBLISHABLE_KEY',
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Supabase Flutter Demo',
home: MyWidget(),
);
}
}
class MyWidget extends StatefulWidget {
const MyWidget({super.key});
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
User? _user;
StreamSubscription<AuthState>? _authSubscription;
@override
void initState() {
super.initState();
_user = Supabase.instance.client.auth.currentUser;
_authSubscription = Supabase.instance.client.auth.onAuthStateChange.listen(
(data) {
setState(() {
_user = data.session?.user;
});
},
onError: (error, stackTrace) {
// Network errors (e.g. offline) are emitted as stream errors.
// Handle or log them here; omitting this handler causes an unhandled
// exception when the device has no connectivity.
},
);
}
@override
void dispose() {
unawaited(_authSubscription?.cancel());
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Profile Example'),
),
body: _user == null ? const _LoginForm() : const _ProfileForm(),
);
}
}
class _LoginForm extends StatefulWidget {
const _LoginForm();
@override
State<_LoginForm> createState() => _LoginFormState();
}
class _LoginFormState extends State<_LoginForm> {
bool _loading = false;
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
super.dispose();
}
Future<void> _signIn() async {
setState(() {
_loading = true;
});
final scaffoldMessenger = ScaffoldMessenger.of(context);
try {
await Supabase.instance.client.auth.signInWithPassword(
email: _emailController.text,
password: _passwordController.text,
);
} catch (e) {
scaffoldMessenger.showSnackBar(
const SnackBar(
content: Text('Login failed'),
backgroundColor: Colors.red,
),
);
if (mounted) {
setState(() {
_loading = false;
});
}
}
}
Future<void> _signUp() async {
setState(() {
_loading = true;
});
final scaffoldMessenger = ScaffoldMessenger.of(context);
try {
await Supabase.instance.client.auth.signUp(
email: _emailController.text,
password: _passwordController.text,
);
} catch (e) {
scaffoldMessenger.showSnackBar(
const SnackBar(
content: Text('Signup failed'),
backgroundColor: Colors.red,
),
);
if (mounted) {
setState(() {
_loading = false;
});
}
}
}
@override
Widget build(BuildContext context) {
return _loading
? const Center(child: CircularProgressIndicator())
: ListView(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
children: [
TextFormField(
keyboardType: TextInputType.emailAddress,
controller: _emailController,
decoration: const InputDecoration(label: Text('Email')),
),
const SizedBox(height: 16),
TextFormField(
obscureText: true,
controller: _passwordController,
decoration: const InputDecoration(label: Text('Password')),
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () => unawaited(_signIn()),
child: const Text('Login'),
),
const SizedBox(height: 16),
TextButton(
onPressed: () => unawaited(_signUp()),
child: const Text('Signup'),
),
],
);
}
}
class _ProfileForm extends StatefulWidget {
const _ProfileForm();
@override
State<_ProfileForm> createState() => _ProfileFormState();
}
class _ProfileFormState extends State<_ProfileForm> {
var _loading = true;
final _usernameController = TextEditingController();
final _websiteController = TextEditingController();
@override
void initState() {
super.initState();
unawaited(_loadProfile());
}
@override
void dispose() {
_usernameController.dispose();
_websiteController.dispose();
super.dispose();
}
Future<void> _loadProfile() async {
final scaffoldMessenger = ScaffoldMessenger.of(context);
try {
final userId = Supabase.instance.client.auth.currentUser!.id;
final data = await Supabase.instance.client
.from('profiles')
.select()
.match({'id': userId})
.maybeSingle();
if (data != null && mounted) {
setState(() {
_usernameController.text = data['username'];
_websiteController.text = data['website'];
});
}
} catch (e) {
scaffoldMessenger.showSnackBar(
const SnackBar(
content: Text('Error occurred while getting profile'),
backgroundColor: Colors.red,
),
);
}
if (mounted) {
setState(() {
_loading = false;
});
}
}
Future<void> _saveProfile() async {
setState(() {
_loading = true;
});
final scaffoldMessenger = ScaffoldMessenger.of(context);
try {
final userId = Supabase.instance.client.auth.currentUser!.id;
await Supabase.instance.client.from('profiles').upsert({
'id': userId,
'username': _usernameController.text,
'website': _websiteController.text,
});
scaffoldMessenger.showSnackBar(
const SnackBar(
content: Text('Saved profile'),
),
);
} catch (e) {
scaffoldMessenger.showSnackBar(
const SnackBar(
content: Text('Error saving profile'),
backgroundColor: Colors.red,
),
);
}
if (mounted) {
setState(() {
_loading = false;
});
}
}
@override
Widget build(BuildContext context) {
return _loading
? const Center(child: CircularProgressIndicator())
: ListView(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
children: [
TextFormField(
controller: _usernameController,
decoration: const InputDecoration(
label: Text('Username'),
),
),
const SizedBox(height: 16),
TextFormField(
controller: _websiteController,
decoration: const InputDecoration(
label: Text('Website'),
),
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () => unawaited(_saveProfile()),
child: const Text('Save'),
),
const SizedBox(height: 16),
TextButton(
onPressed: () => unawaited(
Supabase.instance.client.auth.signOut(),
),
child: const Text('Sign Out'),
),
],
);
}
}