diff --git a/.github/workflows/title-validation.yml b/.github/workflows/title-validation.yml
index 87bedfb3..7e938285 100644
--- a/.github/workflows/title-validation.yml
+++ b/.github/workflows/title-validation.yml
@@ -1,5 +1,5 @@
# See https://github.com/amannn/action-semantic-pull-request
-name: 'PR Title is Conventional'
+name: "PR Title is Conventional"
on:
pull_request:
@@ -33,6 +33,3 @@ jobs:
revert
style
test
- subjectPattern: ^[A-Z].+$
- subjectPatternError: |
- The subject of the PR must begin with an uppercase letter.
diff --git a/packages/supabase_flutter/example/FACEBOOK_AUTH_SETUP.md b/packages/supabase_flutter/example/FACEBOOK_AUTH_SETUP.md
new file mode 100644
index 00000000..9c59649c
--- /dev/null
+++ b/packages/supabase_flutter/example/FACEBOOK_AUTH_SETUP.md
@@ -0,0 +1,98 @@
+# Facebook Authentication Setup
+
+This guide explains how to set up Facebook authentication in the Supabase Examples app.
+
+## Prerequisites
+
+1. A Facebook Developer account
+2. A Facebook App created in the Facebook Developer Console
+3. Supabase project with Facebook OAuth configured
+
+## Setup Steps
+
+### 1. Facebook Developer Console Configuration
+
+1. Go to [Facebook Developers](https://developers.facebook.com/)
+2. Create a new app or use an existing one
+3. Add "Facebook Login" product to your app
+4. Configure your OAuth redirect URIs in Facebook Login settings:
+ - Add your Supabase project's Facebook OAuth callback URL
+ - Format: `https://[your-project-ref].supabase.co/auth/v1/callback`
+
+### 2. Supabase Configuration
+
+1. Go to your Supabase project dashboard
+2. Navigate to Authentication > Providers
+3. Enable Facebook provider
+4. Enter your Facebook App ID and App Secret
+5. Configure the redirect URL if needed
+
+### 3. Flutter App Configuration
+
+#### Android Configuration
+
+The following files have been configured for you:
+
+1. **AndroidManifest.xml** - Contains Facebook SDK configuration
+2. **strings.xml** - Contains placeholder values for Facebook credentials
+
+You need to update the following values in `android/app/src/main/res/values/strings.xml`:
+
+```xml
+YOUR_ACTUAL_FACEBOOK_APP_ID
+YOUR_ACTUAL_FACEBOOK_CLIENT_TOKEN
+fbYOUR_ACTUAL_FACEBOOK_APP_ID
+```
+
+#### iOS Configuration
+
+The following files have been configured for you:
+
+1. **Info.plist** - Contains Facebook SDK configuration
+
+You need to update the following values in `ios/Runner/Info.plist`:
+
+```xml
+FacebookAppID
+YOUR_ACTUAL_FACEBOOK_APP_ID
+FacebookClientToken
+YOUR_ACTUAL_FACEBOOK_CLIENT_TOKEN
+```
+
+And update the URL scheme:
+```xml
+fbYOUR_ACTUAL_FACEBOOK_APP_ID
+```
+
+## Getting Your Facebook Credentials
+
+### App ID
+1. Go to Facebook Developers Console
+2. Select your app
+3. Go to Settings > Basic
+4. Copy the "App ID"
+
+### Client Token
+1. In the same Basic settings page
+2. Copy the "Client Token"
+3. If you don't see it, you may need to generate one
+
+## Testing
+
+1. Replace all placeholder values with your actual Facebook credentials
+2. Run `flutter pub get` to install dependencies
+3. Run the app and test the "Continue with Facebook" button
+4. Ensure your Facebook app is configured to allow the bundle ID/package name of your Flutter app
+
+## Troubleshooting
+
+- **Android**: Make sure your package name in `android/app/build.gradle` matches what's configured in Facebook
+- **iOS**: Make sure your bundle identifier matches what's configured in Facebook
+- **Both platforms**: Ensure your Facebook app is not in "Development Mode" if testing with non-developer accounts
+- Check that your Supabase Facebook OAuth configuration matches your Facebook app settings
+
+## Security Notes
+
+- Never commit real Facebook credentials to version control
+- Consider using environment variables or secure configuration management
+- Regularly rotate your Facebook Client Token
\ No newline at end of file
diff --git a/packages/supabase_flutter/example/README.md b/packages/supabase_flutter/example/README.md
index 1f73590d..9ad118f2 100644
--- a/packages/supabase_flutter/example/README.md
+++ b/packages/supabase_flutter/example/README.md
@@ -1,7 +1,25 @@
-# Profile Example
+# Supabase Flutter Examples
-Basic example of how to signup/login using Supabase auth and read and write from your Supabase
-database.
+Comprehensive examples showcasing the Supabase Flutter library features including authentication, database operations, realtime subscriptions, and file storage.
+
+## Features Included
+
+- **Authentication**: Sign up, sign in, social login (Facebook), password reset, and session management
+- **Database**: CRUD operations with PostgreSQL database
+- **Realtime**: Listen to database changes in real-time using WebSocket connections
+- **Storage**: File upload, download, and management functionality
+
+## Getting Started
+
+1. Set up your Supabase project at [https://supabase.com](https://supabase.com)
+2. Copy your project URL and anon key
+3. Set environment variables or update the default values in `lib/main.dart`:
+ ```
+ SUPABASE_URL=your_supabase_url
+ SUPABASE_ANON_KEY=your_supabase_anon_key
+ ```
+4. Run the SQL setup below in your Supabase SQL editor
+5. For Facebook authentication, follow the Facebook auth setup guide
## SQL
diff --git a/packages/supabase_flutter/example/android/app/build.gradle b/packages/supabase_flutter/example/android/app/build.gradle
index b5511a9a..fab38065 100644
--- a/packages/supabase_flutter/example/android/app/build.gradle
+++ b/packages/supabase_flutter/example/android/app/build.gradle
@@ -11,12 +11,12 @@ android {
ndkVersion = flutter.ndkVersion
compileOptions {
- sourceCompatibility = JavaVersion.VERSION_1_8
- targetCompatibility = JavaVersion.VERSION_1_8
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
- jvmTarget = JavaVersion.VERSION_1_8
+ jvmTarget = JavaVersion.VERSION_11.toString()
}
defaultConfig {
diff --git a/packages/supabase_flutter/example/ios/Runner/Info.plist b/packages/supabase_flutter/example/ios/Runner/Info.plist
index 7f553465..55e9c3bc 100644
--- a/packages/supabase_flutter/example/ios/Runner/Info.plist
+++ b/packages/supabase_flutter/example/ios/Runner/Info.plist
@@ -47,5 +47,41 @@
UIApplicationSupportsIndirectInputEvents
+
+
+ CFBundleURLTypes
+
+
+ CFBundleURLName
+ facebook-login
+ CFBundleURLSchemes
+
+ fbYOUR_FACEBOOK_APP_ID
+
+
+
+ FacebookAppID
+ YOUR_FACEBOOK_APP_ID
+ FacebookClientToken
+ YOUR_FACEBOOK_CLIENT_TOKEN
+ FacebookDisplayName
+ Supabase Flutter Example
+ LSApplicationQueriesSchemes
+
+ fbapi
+ fbapi20130214
+ fbapi20130410
+ fbapi20130702
+ fbapi20131010
+ fbapi20131024
+ fbapi20140410
+ fbapi20140116
+ fbapi20150313
+ fbapi20150629
+ fbauth
+ fbauth2
+ fb-messenger-api
+ fb-messenger-share-api
+
diff --git a/packages/supabase_flutter/example/lib/main.dart b/packages/supabase_flutter/example/lib/main.dart
index eb60a474..30f6a36c 100644
--- a/packages/supabase_flutter/example/lib/main.dart
+++ b/packages/supabase_flutter/example/lib/main.dart
@@ -1,266 +1,39 @@
import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
-Future main() async {
- await Supabase.initialize(url: 'SUPABASE_URL', anonKey: 'SUPABASE_ANON_KEY');
- runApp(const MyApp());
-}
-
-class MyApp extends StatelessWidget {
- const MyApp({Key? key}) : super(key: key);
+import 'screens/home_screen.dart';
- @override
- Widget build(BuildContext context) {
- return const MaterialApp(
- title: 'Supabase Flutter Demo',
- home: MyWidget(),
- );
- }
-}
+Future main() async {
+ WidgetsFlutterBinding.ensureInitialized();
-class MyWidget extends StatefulWidget {
- const MyWidget({Key? key}) : super(key: key);
+ await Supabase.initialize(
+ url: const String.fromEnvironment(
+ 'SUPABASE_URL',
+ defaultValue: 'http://127.0.0.1:54321',
+ ),
+ anonKey: const String.fromEnvironment(
+ 'SUPABASE_ANON_KEY',
+ defaultValue:
+ 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0',
+ ),
+ );
- @override
- State createState() => _MyWidgetState();
+ runApp(const SupabaseFlutterExampleApp());
}
-class _MyWidgetState extends State {
- User? _user;
- @override
- void initState() {
- _getAuth();
- super.initState();
- }
-
- Future _getAuth() async {
- setState(() {
- _user = Supabase.instance.client.auth.currentUser;
- });
- Supabase.instance.client.auth.onAuthStateChange.listen((data) {
- setState(() {
- _user = data.session?.user;
- });
- });
- }
+class SupabaseFlutterExampleApp extends StatelessWidget {
+ const SupabaseFlutterExampleApp({super.key});
@override
Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: const Text('Profile Example'),
+ return MaterialApp(
+ title: 'Supabase Flutter Examples',
+ theme: ThemeData(
+ colorScheme: ColorScheme.fromSeed(seedColor: Colors.green),
+ useMaterial3: true,
),
- body: _user == null ? const _LoginForm() : const _ProfileForm(),
+ home: const HomeScreen(),
);
}
}
-class _LoginForm extends StatefulWidget {
- const _LoginForm({Key? key}) : super(key: key);
-
- @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();
- }
-
- @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: () async {
- setState(() {
- _loading = true;
- });
- final ScaffoldMessengerState scaffoldMessenger =
- ScaffoldMessenger.of(context);
- try {
- final email = _emailController.text;
- final password = _passwordController.text;
- await Supabase.instance.client.auth.signInWithPassword(
- email: email,
- password: password,
- );
- } catch (e) {
- scaffoldMessenger.showSnackBar(const SnackBar(
- content: Text('Login failed'),
- backgroundColor: Colors.red,
- ));
- setState(() {
- _loading = false;
- });
- }
- },
- child: const Text('Login'),
- ),
- const SizedBox(height: 16),
- TextButton(
- onPressed: () async {
- setState(() {
- _loading = true;
- });
- final ScaffoldMessengerState scaffoldMessenger =
- ScaffoldMessenger.of(context);
- try {
- final email = _emailController.text;
- final password = _passwordController.text;
- await Supabase.instance.client.auth.signUp(
- email: email,
- password: password,
- );
- } catch (e) {
- scaffoldMessenger.showSnackBar(const SnackBar(
- content: Text('Signup failed'),
- backgroundColor: Colors.red,
- ));
- setState(() {
- _loading = false;
- });
- }
- },
- child: const Text('Signup'),
- ),
- ],
- );
- }
-}
-
-class _ProfileForm extends StatefulWidget {
- const _ProfileForm({Key? key}) : super(key: key);
-
- @override
- State<_ProfileForm> createState() => _ProfileFormState();
-}
-
-class _ProfileFormState extends State<_ProfileForm> {
- var _loading = true;
- final _usernameController = TextEditingController();
- final _websiteController = TextEditingController();
-
- @override
- void initState() {
- _loadProfile();
- super.initState();
- }
-
- @override
- void dispose() {
- _usernameController.dispose();
- _websiteController.dispose();
- super.dispose();
- }
-
- Future _loadProfile() async {
- final ScaffoldMessengerState 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) {
- 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,
- ));
- }
- 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: () async {
- final ScaffoldMessengerState scaffoldMessenger =
- ScaffoldMessenger.of(context);
- try {
- setState(() {
- _loading = true;
- });
- final userId =
- Supabase.instance.client.auth.currentUser!.id;
- final username = _usernameController.text;
- final website = _websiteController.text;
- await Supabase.instance.client.from('profiles').upsert({
- 'id': userId,
- 'username': username,
- 'website': website,
- });
- if (mounted) {
- scaffoldMessenger.showSnackBar(const SnackBar(
- content: Text('Saved profile'),
- ));
- }
- } catch (e) {
- scaffoldMessenger.showSnackBar(const SnackBar(
- content: Text('Error saving profile'),
- backgroundColor: Colors.red,
- ));
- }
- setState(() {
- _loading = false;
- });
- },
- child: const Text('Save')),
- const SizedBox(height: 16),
- TextButton(
- onPressed: () => Supabase.instance.client.auth.signOut(),
- child: const Text('Sign Out')),
- ],
- );
- }
-}
diff --git a/packages/supabase_flutter/example/lib/screens/auth_screen.dart b/packages/supabase_flutter/example/lib/screens/auth_screen.dart
new file mode 100644
index 00000000..74459a10
--- /dev/null
+++ b/packages/supabase_flutter/example/lib/screens/auth_screen.dart
@@ -0,0 +1,346 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';
+import 'package:supabase_flutter/supabase_flutter.dart';
+
+class AuthScreen extends StatefulWidget {
+ const AuthScreen({super.key});
+
+ @override
+ State createState() => _AuthScreenState();
+}
+
+class _AuthScreenState extends State {
+ final _emailController = TextEditingController();
+ final _passwordController = TextEditingController();
+ bool _loading = false;
+ User? _user;
+
+ @override
+ void initState() {
+ super.initState();
+ _user = Supabase.instance.client.auth.currentUser;
+ _setupAuthListener();
+ }
+
+ void _setupAuthListener() {
+ Supabase.instance.client.auth.onAuthStateChange.listen((data) {
+ if (mounted) {
+ setState(() {
+ _user = data.session?.user;
+ });
+ }
+ });
+ }
+
+ @override
+ void dispose() {
+ _emailController.dispose();
+ _passwordController.dispose();
+ super.dispose();
+ }
+
+ Future _signUp() async {
+ setState(() => _loading = true);
+
+ try {
+ final response = await Supabase.instance.client.auth.signUp(
+ email: _emailController.text,
+ password: _passwordController.text,
+ );
+
+ if (mounted) {
+ if (response.user != null && response.session == null) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(
+ content: Text('Check your email for confirmation!'),
+ backgroundColor: Colors.green,
+ ),
+ );
+ } else {
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(
+ content: Text('Sign up successful!'),
+ backgroundColor: Colors.green,
+ ),
+ );
+ }
+ }
+ } catch (e) {
+ if (mounted) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Text('Sign up failed: ${e.toString()}'),
+ backgroundColor: Colors.red,
+ ),
+ );
+ }
+ } finally {
+ if (mounted) setState(() => _loading = false);
+ }
+ }
+
+ Future _signIn() async {
+ setState(() => _loading = true);
+
+ try {
+ await Supabase.instance.client.auth.signInWithPassword(
+ email: _emailController.text,
+ password: _passwordController.text,
+ );
+
+ if (mounted) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(
+ content: Text('Sign in successful!'),
+ backgroundColor: Colors.green,
+ ),
+ );
+ }
+ } catch (e) {
+ if (mounted) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Text('Sign in failed: ${e.toString()}'),
+ backgroundColor: Colors.red,
+ ),
+ );
+ }
+ } finally {
+ if (mounted) setState(() => _loading = false);
+ }
+ }
+
+ Future _signOut() async {
+ setState(() => _loading = true);
+
+ try {
+ await Supabase.instance.client.auth.signOut();
+
+ if (mounted) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(
+ content: Text('Signed out successfully!'),
+ backgroundColor: Colors.green,
+ ),
+ );
+ }
+ } catch (e) {
+ if (mounted) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Text('Sign out failed: ${e.toString()}'),
+ backgroundColor: Colors.red,
+ ),
+ );
+ }
+ } finally {
+ if (mounted) setState(() => _loading = false);
+ }
+ }
+
+ Future _signInWithFacebook() async {
+ setState(() => _loading = true);
+
+ try {
+ final LoginResult result = await FacebookAuth.instance.login(
+ permissions: ['email', 'public_profile'],
+ );
+
+ if (result.status == LoginStatus.success) {
+ final accessToken = result.accessToken!.tokenString;
+
+ await Supabase.instance.client.auth.signInWithIdToken(
+ provider: OAuthProvider.facebook,
+ idToken: accessToken,
+ );
+
+ if (mounted) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(
+ content: Text('Facebook sign in successful!'),
+ backgroundColor: Colors.green,
+ ),
+ );
+ }
+ } else {
+ if (mounted) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Text('Facebook sign in cancelled: ${result.status}'),
+ backgroundColor: Colors.orange,
+ ),
+ );
+ }
+ }
+ } catch (e) {
+ if (mounted) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Text('Facebook sign in failed: ${e.toString()}'),
+ backgroundColor: Colors.red,
+ ),
+ );
+ }
+ } finally {
+ if (mounted) setState(() => _loading = false);
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('Authentication Examples'),
+ backgroundColor: Theme.of(context).colorScheme.inversePrimary,
+ ),
+ body: Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ Text(
+ 'Authentication Status',
+ style: Theme.of(context).textTheme.headlineSmall,
+ ),
+ const SizedBox(height: 16),
+ Card(
+ child: Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ 'Current User:',
+ style: Theme.of(context).textTheme.titleMedium,
+ ),
+ const SizedBox(height: 8),
+ if (_user != null) ...[
+ Text('Email: ${_user!.email ?? 'N/A'}'),
+ Text('ID: ${_user!.id}'),
+ Text('Created: ${_user!.createdAt}'),
+ const SizedBox(height: 16),
+ ElevatedButton(
+ onPressed: _loading ? null : _signOut,
+ child: _loading
+ ? const SizedBox(
+ width: 16,
+ height: 16,
+ child: CircularProgressIndicator(strokeWidth: 2),
+ )
+ : const Text('Sign Out'),
+ ),
+ ] else ...[
+ const Text('Not authenticated'),
+ ],
+ ],
+ ),
+ ),
+ ),
+ const SizedBox(height: 24),
+ if (_user == null) ...[
+ Text(
+ 'Sign In / Sign Up',
+ style: Theme.of(context).textTheme.headlineSmall,
+ ),
+ const SizedBox(height: 16),
+ TextFormField(
+ controller: _emailController,
+ keyboardType: TextInputType.emailAddress,
+ decoration: const InputDecoration(
+ labelText: 'Email',
+ border: OutlineInputBorder(),
+ prefixIcon: Icon(Icons.email),
+ ),
+ ),
+ const SizedBox(height: 16),
+ TextFormField(
+ controller: _passwordController,
+ obscureText: true,
+ decoration: const InputDecoration(
+ labelText: 'Password',
+ border: OutlineInputBorder(),
+ prefixIcon: Icon(Icons.lock),
+ ),
+ ),
+ const SizedBox(height: 24),
+ Row(
+ children: [
+ Expanded(
+ child: ElevatedButton(
+ onPressed: _loading ? null : _signIn,
+ style: ElevatedButton.styleFrom(
+ backgroundColor: Colors.blue,
+ foregroundColor: Colors.white,
+ ),
+ child: _loading
+ ? const SizedBox(
+ width: 16,
+ height: 16,
+ child: CircularProgressIndicator(
+ strokeWidth: 2,
+ valueColor: AlwaysStoppedAnimation(Colors.white),
+ ),
+ )
+ : const Text('Sign In'),
+ ),
+ ),
+ const SizedBox(width: 16),
+ Expanded(
+ child: ElevatedButton(
+ onPressed: _loading ? null : _signUp,
+ style: ElevatedButton.styleFrom(
+ backgroundColor: Colors.green,
+ foregroundColor: Colors.white,
+ ),
+ child: _loading
+ ? const SizedBox(
+ width: 16,
+ height: 16,
+ child: CircularProgressIndicator(
+ strokeWidth: 2,
+ valueColor: AlwaysStoppedAnimation(Colors.white),
+ ),
+ )
+ : const Text('Sign Up'),
+ ),
+ ),
+ ],
+ ),
+ const SizedBox(height: 24),
+ const Divider(),
+ const SizedBox(height: 16),
+ Text(
+ 'Or sign in with:',
+ style: Theme.of(context).textTheme.titleMedium,
+ textAlign: TextAlign.center,
+ ),
+ const SizedBox(height: 16),
+ SizedBox(
+ width: double.infinity,
+ child: ElevatedButton.icon(
+ onPressed: _loading ? null : _signInWithFacebook,
+ icon: const Icon(Icons.facebook, color: Colors.white),
+ label: _loading
+ ? const SizedBox(
+ width: 16,
+ height: 16,
+ child: CircularProgressIndicator(
+ strokeWidth: 2,
+ valueColor: AlwaysStoppedAnimation(Colors.white),
+ ),
+ )
+ : const Text('Continue with Facebook'),
+ style: ElevatedButton.styleFrom(
+ backgroundColor: const Color(0xFF1877F2),
+ foregroundColor: Colors.white,
+ padding: const EdgeInsets.symmetric(vertical: 12),
+ ),
+ ),
+ ),
+ ],
+ ],
+ ),
+ ),
+ );
+ }
+}
\ No newline at end of file
diff --git a/packages/supabase_flutter/example/lib/screens/database_screen.dart b/packages/supabase_flutter/example/lib/screens/database_screen.dart
new file mode 100644
index 00000000..e8ef015a
--- /dev/null
+++ b/packages/supabase_flutter/example/lib/screens/database_screen.dart
@@ -0,0 +1,327 @@
+import 'package:flutter/material.dart';
+import 'package:supabase_flutter/supabase_flutter.dart';
+
+class DatabaseScreen extends StatefulWidget {
+ const DatabaseScreen({super.key});
+
+ @override
+ State createState() => _DatabaseScreenState();
+}
+
+class _DatabaseScreenState extends State {
+ final _titleController = TextEditingController();
+ final _descriptionController = TextEditingController();
+ List