├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── io │ │ │ │ └── supabase │ │ │ │ └── supabase_demo │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── launcher_icon.png │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets └── images │ ├── logo-dark.png │ ├── logo.png │ └── noavatar.jpeg ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── lib ├── components │ ├── auth_required_state.dart │ └── auth_state.dart ├── configure_nonweb.dart ├── configure_web.dart ├── main.dart ├── screens │ ├── change_password.dart │ ├── forgot_password.dart │ ├── profile_screen.dart │ ├── signin_screen.dart │ ├── signup_screen.dart │ ├── splash_screen.dart │ └── web_home_screen.dart └── utils │ ├── constants.dart │ └── helpers.dart ├── pubspec.lock ├── pubspec.yaml ├── test └── widget_test.dart └── web ├── favicon.ico ├── icons ├── Icon-192.png └── Icon-512.png ├── index.html └── manifest.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | .vscode/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | 35 | # Web related 36 | lib/generated_plugin_registrant.dart 37 | 38 | # Symbolication related 39 | app.*.symbols 40 | 41 | # Obfuscation related 42 | app.*.map.json 43 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 9b2d32b605630f28625709ebd9d78ab3016b2bf6 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Supabase Flutter User Management 2 | 3 | This example will set you up for a very common situation: users can sign up with a magic link and then update their account with public profile information, including a profile image. 4 | 5 | ## Technologies used 6 | 7 | - Frontend: 8 | - [Flutter SDK](https://flutter.dev/) - Google's UI toolkit for crafting beautiful, natively compiled applications for mobile, web, and desktop from a single codebase. 9 | - [supabase_flutter](https://pub.dev/packages/supabase_flutter) for user management and image storage uploading. 10 | - Backend: 11 | - [app.supabase.io](https://app.supabase.io/): hosted Postgres database with restful API for usage with supabase_flutter. 12 | 13 | ## Build from scratch 14 | 15 | ### 1. Create new project 16 | 17 | Sign up to Supabase - [https://app.supabase.io](https://app.supabase.io) and create a new project. Wait for your database to start. 18 | 19 | ### 2. Run "User Management" Quickstart 20 | 21 | Once your database has started, run the "User Management Starter" quickstart. Inside of your project, enter the `SQL editor` tab and scroll down until you see `User Management Starter: Set up a Public Profiles table which you can access with your API`. 22 | 23 | ### 3. Get the URL and Key 24 | 25 | Go to the Project Settings (the cog icon), open the API tab, and find your API URL and `anon` key, you'll need these in the next step. 26 | 27 | The `anon` key is your client-side API key. It allows "anonymous access" to your database, until the user has logged in. Once they have logged in, the keys will switch to the user's own login token. This enables row level security for your data. Read more about this [below](#postgres-row-level-security). 28 | 29 | ![image](https://user-images.githubusercontent.com/10214025/88916245-528c2680-d298-11ea-8a71-708f93e1ce4f.png) 30 | 31 | **_NOTE_**: The `service_role` key has full access to your data, bypassing any security policies. These keys have to be kept secret and are meant to be used in server environments and never on a client or browser. 32 | 33 | ### 4. Setup deeplink redirect urls 34 | 35 | Go to the Authentication Settings page (the user icon). Enter the flutter app redirect url below into `Additional Redirect URLs` field. Then click Save. 36 | 37 | > io.supabase.flutterdemo://login-callback 38 | 39 | ![authentication settings page](https://user-images.githubusercontent.com/689843/124574731-f735c180-de74-11eb-8f50-2d34161261dd.png) 40 | 41 | ### 5. Setup Github 3rd party logins 42 | 43 | Follow the guide https://supabase.io/docs/guides/auth#third-party-logins 44 | 45 | ### 6. Run the flutter application 46 | 47 | - Go to `lib/utils/constants.dart` file 48 | - Update `SUPABASE_URL` and `SUPABASE_ANNON_KEY` with your URL and Key 49 | - Run the application: `flutter run` 50 | 51 | ## Supabase details 52 | 53 | ### Postgres Row level security 54 | 55 | This project uses very high-level Authorization using Postgres' Role Level Security. 56 | When you start a Postgres database on Supabase, we populate it with an `auth` schema, and some helper functions. 57 | When a user logs in, they are issued a JWT with the role `authenticated` and thier UUID. 58 | We can use these details to provide fine-grained control over what each user can and cannot do. 59 | 60 | This is a trimmed-down schema, with the policies: 61 | 62 | ```sql 63 | -- Create a table for Public Profiles 64 | create table profiles ( 65 | id uuid references auth.users not null, 66 | updated_at timestamp with time zone, 67 | username text unique, 68 | avatar_url text, 69 | website text, 70 | 71 | primary key (id), 72 | unique(username), 73 | constraint username_length check (char_length(username) >= 3) 74 | ); 75 | 76 | alter table profiles enable row level security; 77 | 78 | create policy "Public profiles are viewable by everyone." 79 | on profiles for select 80 | using ( true ); 81 | 82 | create policy "Users can insert their own profile." 83 | on profiles for insert 84 | with check ( auth.uid() = id ); 85 | 86 | create policy "Users can update own profile." 87 | on profiles for update 88 | using ( auth.uid() = id ); 89 | 90 | -- Set up Realtime! 91 | begin; 92 | drop publication if exists supabase_realtime; 93 | create publication supabase_realtime; 94 | commit; 95 | alter publication supabase_realtime add table profiles; 96 | 97 | -- Set up Storage! 98 | insert into storage.buckets (id, name) 99 | values ('avatars', 'avatars'); 100 | 101 | create policy "Avatar images are publicly accessible." 102 | on storage.objects for select 103 | using ( bucket_id = 'avatars' ); 104 | 105 | create policy "Anyone can upload an avatar." 106 | on storage.objects for insert 107 | with check ( bucket_id = 'avatars' ); 108 | 109 | create policy "Anyone can update an avatar." 110 | on storage.objects for update 111 | with check ( bucket_id = 'avatars' ); 112 | ``` 113 | 114 | ## Authors 115 | 116 | - [Supabase](https://supabase.io) 117 | 118 | Supabase is open source. We'd love for you to follow along and get involved at https://github.com/supabase/supabase 119 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:lint/analysis_options.yaml 2 | 3 | linter: 4 | rules: 5 | avoid_print: false -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 30 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | defaultConfig { 36 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 37 | applicationId "io.supabase.supabase_demo" 38 | minSdkVersion 18 39 | targetSdkVersion 30 40 | versionCode flutterVersionCode.toInteger() 41 | versionName flutterVersionName 42 | } 43 | 44 | buildTypes { 45 | release { 46 | // TODO: Add your own signing config for the release build. 47 | // Signing with the debug keys for now, so `flutter run --release` works. 48 | signingConfig signingConfigs.debug 49 | } 50 | } 51 | } 52 | 53 | flutter { 54 | source '../..' 55 | } 56 | 57 | dependencies { 58 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 59 | } 60 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 15 | 19 | 23 | 28 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 48 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/io/supabase/supabase_demo/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package io.supabase.supabase_demo 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/android/app/src/main/res/mipmap-hdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/android/app/src/main/res/mipmap-mdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.1.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /assets/images/logo-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/assets/images/logo-dark.png -------------------------------------------------------------------------------- /assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/assets/images/logo.png -------------------------------------------------------------------------------- /assets/images/noavatar.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/assets/images/noavatar.jpeg -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/ephemeral/ 22 | Flutter/app.flx 23 | Flutter/app.zip 24 | Flutter/flutter_assets/ 25 | Flutter/flutter_export_environment.sh 26 | ServiceDefinitions.json 27 | Runner/GeneratedPluginRegistrant.* 28 | 29 | # Exceptions to above rules. 30 | !default.mode1v3 31 | !default.mode2v3 32 | !default.pbxuser 33 | !default.perspectivev3 34 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXCopyFilesBuildPhase section */ 19 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 20 | isa = PBXCopyFilesBuildPhase; 21 | buildActionMask = 2147483647; 22 | dstPath = ""; 23 | dstSubfolderSpec = 10; 24 | files = ( 25 | ); 26 | name = "Embed Frameworks"; 27 | runOnlyForDeploymentPostprocessing = 0; 28 | }; 29 | /* End PBXCopyFilesBuildPhase section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 33 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 34 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 35 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 36 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 37 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 38 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 39 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 40 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 42 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 43 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 44 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | ); 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXFrameworksBuildPhase section */ 56 | 57 | /* Begin PBXGroup section */ 58 | 9740EEB11CF90186004384FC /* Flutter */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 62 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 63 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 64 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 65 | ); 66 | name = Flutter; 67 | sourceTree = ""; 68 | }; 69 | 97C146E51CF9000F007C117D = { 70 | isa = PBXGroup; 71 | children = ( 72 | 9740EEB11CF90186004384FC /* Flutter */, 73 | 97C146F01CF9000F007C117D /* Runner */, 74 | 97C146EF1CF9000F007C117D /* Products */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | 97C146EF1CF9000F007C117D /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 97C146EE1CF9000F007C117D /* Runner.app */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | 97C146F01CF9000F007C117D /* Runner */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 90 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 91 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 92 | 97C147021CF9000F007C117D /* Info.plist */, 93 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 94 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 95 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 96 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 97 | ); 98 | path = Runner; 99 | sourceTree = ""; 100 | }; 101 | /* End PBXGroup section */ 102 | 103 | /* Begin PBXNativeTarget section */ 104 | 97C146ED1CF9000F007C117D /* Runner */ = { 105 | isa = PBXNativeTarget; 106 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 107 | buildPhases = ( 108 | 9740EEB61CF901F6004384FC /* Run Script */, 109 | 97C146EA1CF9000F007C117D /* Sources */, 110 | 97C146EB1CF9000F007C117D /* Frameworks */, 111 | 97C146EC1CF9000F007C117D /* Resources */, 112 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 113 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 114 | ); 115 | buildRules = ( 116 | ); 117 | dependencies = ( 118 | ); 119 | name = Runner; 120 | productName = Runner; 121 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 122 | productType = "com.apple.product-type.application"; 123 | }; 124 | /* End PBXNativeTarget section */ 125 | 126 | /* Begin PBXProject section */ 127 | 97C146E61CF9000F007C117D /* Project object */ = { 128 | isa = PBXProject; 129 | attributes = { 130 | LastUpgradeCheck = 1020; 131 | ORGANIZATIONNAME = ""; 132 | TargetAttributes = { 133 | 97C146ED1CF9000F007C117D = { 134 | CreatedOnToolsVersion = 7.3.1; 135 | LastSwiftMigration = 1100; 136 | }; 137 | }; 138 | }; 139 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 140 | compatibilityVersion = "Xcode 9.3"; 141 | developmentRegion = en; 142 | hasScannedForEncodings = 0; 143 | knownRegions = ( 144 | en, 145 | Base, 146 | ); 147 | mainGroup = 97C146E51CF9000F007C117D; 148 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 149 | projectDirPath = ""; 150 | projectRoot = ""; 151 | targets = ( 152 | 97C146ED1CF9000F007C117D /* Runner */, 153 | ); 154 | }; 155 | /* End PBXProject section */ 156 | 157 | /* Begin PBXResourcesBuildPhase section */ 158 | 97C146EC1CF9000F007C117D /* Resources */ = { 159 | isa = PBXResourcesBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 163 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 164 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 165 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXResourcesBuildPhase section */ 170 | 171 | /* Begin PBXShellScriptBuildPhase section */ 172 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 173 | isa = PBXShellScriptBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | ); 177 | inputPaths = ( 178 | ); 179 | name = "Thin Binary"; 180 | outputPaths = ( 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | shellPath = /bin/sh; 184 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 185 | }; 186 | 9740EEB61CF901F6004384FC /* Run Script */ = { 187 | isa = PBXShellScriptBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | ); 191 | inputPaths = ( 192 | ); 193 | name = "Run Script"; 194 | outputPaths = ( 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | shellPath = /bin/sh; 198 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 199 | }; 200 | /* End PBXShellScriptBuildPhase section */ 201 | 202 | /* Begin PBXSourcesBuildPhase section */ 203 | 97C146EA1CF9000F007C117D /* Sources */ = { 204 | isa = PBXSourcesBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 208 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | }; 212 | /* End PBXSourcesBuildPhase section */ 213 | 214 | /* Begin PBXVariantGroup section */ 215 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 216 | isa = PBXVariantGroup; 217 | children = ( 218 | 97C146FB1CF9000F007C117D /* Base */, 219 | ); 220 | name = Main.storyboard; 221 | sourceTree = ""; 222 | }; 223 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 224 | isa = PBXVariantGroup; 225 | children = ( 226 | 97C147001CF9000F007C117D /* Base */, 227 | ); 228 | name = LaunchScreen.storyboard; 229 | sourceTree = ""; 230 | }; 231 | /* End PBXVariantGroup section */ 232 | 233 | /* Begin XCBuildConfiguration section */ 234 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 235 | isa = XCBuildConfiguration; 236 | buildSettings = { 237 | ALWAYS_SEARCH_USER_PATHS = NO; 238 | CLANG_ANALYZER_NONNULL = YES; 239 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 240 | CLANG_CXX_LIBRARY = "libc++"; 241 | CLANG_ENABLE_MODULES = YES; 242 | CLANG_ENABLE_OBJC_ARC = YES; 243 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 244 | CLANG_WARN_BOOL_CONVERSION = YES; 245 | CLANG_WARN_COMMA = YES; 246 | CLANG_WARN_CONSTANT_CONVERSION = YES; 247 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 248 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 249 | CLANG_WARN_EMPTY_BODY = YES; 250 | CLANG_WARN_ENUM_CONVERSION = YES; 251 | CLANG_WARN_INFINITE_RECURSION = YES; 252 | CLANG_WARN_INT_CONVERSION = YES; 253 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 254 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 255 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 256 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 257 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 258 | CLANG_WARN_STRICT_PROTOTYPES = YES; 259 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 260 | CLANG_WARN_UNREACHABLE_CODE = YES; 261 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 262 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 263 | COPY_PHASE_STRIP = NO; 264 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 265 | ENABLE_NS_ASSERTIONS = NO; 266 | ENABLE_STRICT_OBJC_MSGSEND = YES; 267 | GCC_C_LANGUAGE_STANDARD = gnu99; 268 | GCC_NO_COMMON_BLOCKS = YES; 269 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 270 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 271 | GCC_WARN_UNDECLARED_SELECTOR = YES; 272 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 273 | GCC_WARN_UNUSED_FUNCTION = YES; 274 | GCC_WARN_UNUSED_VARIABLE = YES; 275 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 276 | MTL_ENABLE_DEBUG_INFO = NO; 277 | SDKROOT = iphoneos; 278 | SUPPORTED_PLATFORMS = iphoneos; 279 | TARGETED_DEVICE_FAMILY = "1,2"; 280 | VALIDATE_PRODUCT = YES; 281 | }; 282 | name = Profile; 283 | }; 284 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 285 | isa = XCBuildConfiguration; 286 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 287 | buildSettings = { 288 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 289 | CLANG_ENABLE_MODULES = YES; 290 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 291 | ENABLE_BITCODE = NO; 292 | INFOPLIST_FILE = Runner/Info.plist; 293 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 294 | PRODUCT_BUNDLE_IDENTIFIER = io.supabase.supabaseDemo; 295 | PRODUCT_NAME = "$(TARGET_NAME)"; 296 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 297 | SWIFT_VERSION = 5.0; 298 | VERSIONING_SYSTEM = "apple-generic"; 299 | }; 300 | name = Profile; 301 | }; 302 | 97C147031CF9000F007C117D /* Debug */ = { 303 | isa = XCBuildConfiguration; 304 | buildSettings = { 305 | ALWAYS_SEARCH_USER_PATHS = NO; 306 | CLANG_ANALYZER_NONNULL = YES; 307 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 308 | CLANG_CXX_LIBRARY = "libc++"; 309 | CLANG_ENABLE_MODULES = YES; 310 | CLANG_ENABLE_OBJC_ARC = YES; 311 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 312 | CLANG_WARN_BOOL_CONVERSION = YES; 313 | CLANG_WARN_COMMA = YES; 314 | CLANG_WARN_CONSTANT_CONVERSION = YES; 315 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 316 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 317 | CLANG_WARN_EMPTY_BODY = YES; 318 | CLANG_WARN_ENUM_CONVERSION = YES; 319 | CLANG_WARN_INFINITE_RECURSION = YES; 320 | CLANG_WARN_INT_CONVERSION = YES; 321 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 322 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 323 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 324 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 325 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 326 | CLANG_WARN_STRICT_PROTOTYPES = YES; 327 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 328 | CLANG_WARN_UNREACHABLE_CODE = YES; 329 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 330 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 331 | COPY_PHASE_STRIP = NO; 332 | DEBUG_INFORMATION_FORMAT = dwarf; 333 | ENABLE_STRICT_OBJC_MSGSEND = YES; 334 | ENABLE_TESTABILITY = YES; 335 | GCC_C_LANGUAGE_STANDARD = gnu99; 336 | GCC_DYNAMIC_NO_PIC = NO; 337 | GCC_NO_COMMON_BLOCKS = YES; 338 | GCC_OPTIMIZATION_LEVEL = 0; 339 | GCC_PREPROCESSOR_DEFINITIONS = ( 340 | "DEBUG=1", 341 | "$(inherited)", 342 | ); 343 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 344 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 345 | GCC_WARN_UNDECLARED_SELECTOR = YES; 346 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 347 | GCC_WARN_UNUSED_FUNCTION = YES; 348 | GCC_WARN_UNUSED_VARIABLE = YES; 349 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 350 | MTL_ENABLE_DEBUG_INFO = YES; 351 | ONLY_ACTIVE_ARCH = YES; 352 | SDKROOT = iphoneos; 353 | TARGETED_DEVICE_FAMILY = "1,2"; 354 | }; 355 | name = Debug; 356 | }; 357 | 97C147041CF9000F007C117D /* Release */ = { 358 | isa = XCBuildConfiguration; 359 | buildSettings = { 360 | ALWAYS_SEARCH_USER_PATHS = NO; 361 | CLANG_ANALYZER_NONNULL = YES; 362 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 363 | CLANG_CXX_LIBRARY = "libc++"; 364 | CLANG_ENABLE_MODULES = YES; 365 | CLANG_ENABLE_OBJC_ARC = YES; 366 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 367 | CLANG_WARN_BOOL_CONVERSION = YES; 368 | CLANG_WARN_COMMA = YES; 369 | CLANG_WARN_CONSTANT_CONVERSION = YES; 370 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 371 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 372 | CLANG_WARN_EMPTY_BODY = YES; 373 | CLANG_WARN_ENUM_CONVERSION = YES; 374 | CLANG_WARN_INFINITE_RECURSION = YES; 375 | CLANG_WARN_INT_CONVERSION = YES; 376 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 377 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 378 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 379 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 380 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 381 | CLANG_WARN_STRICT_PROTOTYPES = YES; 382 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 383 | CLANG_WARN_UNREACHABLE_CODE = YES; 384 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 385 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 386 | COPY_PHASE_STRIP = NO; 387 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 388 | ENABLE_NS_ASSERTIONS = NO; 389 | ENABLE_STRICT_OBJC_MSGSEND = YES; 390 | GCC_C_LANGUAGE_STANDARD = gnu99; 391 | GCC_NO_COMMON_BLOCKS = YES; 392 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 393 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 394 | GCC_WARN_UNDECLARED_SELECTOR = YES; 395 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 396 | GCC_WARN_UNUSED_FUNCTION = YES; 397 | GCC_WARN_UNUSED_VARIABLE = YES; 398 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 399 | MTL_ENABLE_DEBUG_INFO = NO; 400 | SDKROOT = iphoneos; 401 | SUPPORTED_PLATFORMS = iphoneos; 402 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 403 | TARGETED_DEVICE_FAMILY = "1,2"; 404 | VALIDATE_PRODUCT = YES; 405 | }; 406 | name = Release; 407 | }; 408 | 97C147061CF9000F007C117D /* Debug */ = { 409 | isa = XCBuildConfiguration; 410 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 411 | buildSettings = { 412 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 413 | CLANG_ENABLE_MODULES = YES; 414 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 415 | ENABLE_BITCODE = NO; 416 | INFOPLIST_FILE = Runner/Info.plist; 417 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 418 | PRODUCT_BUNDLE_IDENTIFIER = io.supabase.supabaseDemo; 419 | PRODUCT_NAME = "$(TARGET_NAME)"; 420 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 421 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 422 | SWIFT_VERSION = 5.0; 423 | VERSIONING_SYSTEM = "apple-generic"; 424 | }; 425 | name = Debug; 426 | }; 427 | 97C147071CF9000F007C117D /* Release */ = { 428 | isa = XCBuildConfiguration; 429 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 430 | buildSettings = { 431 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 432 | CLANG_ENABLE_MODULES = YES; 433 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 434 | ENABLE_BITCODE = NO; 435 | INFOPLIST_FILE = Runner/Info.plist; 436 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 437 | PRODUCT_BUNDLE_IDENTIFIER = io.supabase.supabaseDemo; 438 | PRODUCT_NAME = "$(TARGET_NAME)"; 439 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 440 | SWIFT_VERSION = 5.0; 441 | VERSIONING_SYSTEM = "apple-generic"; 442 | }; 443 | name = Release; 444 | }; 445 | /* End XCBuildConfiguration section */ 446 | 447 | /* Begin XCConfigurationList section */ 448 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 449 | isa = XCConfigurationList; 450 | buildConfigurations = ( 451 | 97C147031CF9000F007C117D /* Debug */, 452 | 97C147041CF9000F007C117D /* Release */, 453 | 249021D3217E4FDB00AE95B9 /* Profile */, 454 | ); 455 | defaultConfigurationIsVisible = 0; 456 | defaultConfigurationName = Release; 457 | }; 458 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 459 | isa = XCConfigurationList; 460 | buildConfigurations = ( 461 | 97C147061CF9000F007C117D /* Debug */, 462 | 97C147071CF9000F007C117D /* Release */, 463 | 249021D4217E4FDB00AE95B9 /* Profile */, 464 | ); 465 | defaultConfigurationIsVisible = 0; 466 | defaultConfigurationName = Release; 467 | }; 468 | /* End XCConfigurationList section */ 469 | }; 470 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 471 | } -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | Supabase Demo 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | CFBundleURLTypes 45 | 46 | 47 | CFBundleTypeRole 48 | Editor 49 | CFBundleURLSchemes 50 | 51 | 52 | io.supabase.flutterdemo 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/components/auth_required_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:supabase_flutter/supabase_flutter.dart'; 3 | 4 | class AuthRequiredState 5 | extends SupabaseAuthRequiredState { 6 | @override 7 | void onUnauthenticated() { 8 | Navigator.pushNamedAndRemoveUntil(context, '/signIn', (route) => false); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /lib/components/auth_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:supabase/supabase.dart' as supabase; 3 | import 'package:supabase_flutter/supabase_flutter.dart'; 4 | 5 | class AuthState extends SupabaseAuthState { 6 | @override 7 | void onUnauthenticated() { 8 | Navigator.pushNamedAndRemoveUntil(context, '/signIn', (route) => false); 9 | } 10 | 11 | @override 12 | void onAuthenticated(supabase.Session session) { 13 | Navigator.pushNamedAndRemoveUntil(context, '/profile', (route) => false); 14 | } 15 | 16 | @override 17 | void onPasswordRecovery(supabase.Session session) { 18 | Navigator.pushNamedAndRemoveUntil( 19 | context, '/profile/changePassword', (route) => false); 20 | } 21 | 22 | @override 23 | void onErrorAuthenticating(String message) { 24 | print('***** onErrorAuthenticating: $message'); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/configure_nonweb.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_secure_storage/flutter_secure_storage.dart'; 2 | import 'package:supabase_flutter/supabase_flutter.dart'; 3 | 4 | import 'utils/constants.dart'; 5 | 6 | Future configureApp() async { 7 | // init Supabase singleton 8 | await Supabase.initialize( 9 | url: supabaseUrl, 10 | anonKey: supabaseAnnonKey, 11 | authCallbackUrlHostname: 'login-callback', 12 | debug: true, 13 | localStorage: SecureLocalStorage(), 14 | ); 15 | } 16 | 17 | // user flutter_secure_storage to persist user session 18 | class SecureLocalStorage extends LocalStorage { 19 | SecureLocalStorage() 20 | : super( 21 | initialize: () async {}, 22 | hasAccessToken: () { 23 | const storage = FlutterSecureStorage(); 24 | return storage.containsKey(key: supabasePersistSessionKey); 25 | }, 26 | accessToken: () { 27 | const storage = FlutterSecureStorage(); 28 | return storage.read(key: supabasePersistSessionKey); 29 | }, 30 | removePersistedSession: () { 31 | const storage = FlutterSecureStorage(); 32 | return storage.delete(key: supabasePersistSessionKey); 33 | }, 34 | persistSession: (String value) { 35 | const storage = FlutterSecureStorage(); 36 | return storage.write(key: supabasePersistSessionKey, value: value); 37 | }, 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /lib/configure_web.dart: -------------------------------------------------------------------------------- 1 | import 'package:supabase_flutter/supabase_flutter.dart'; 2 | 3 | import 'utils/constants.dart'; 4 | 5 | Future configureApp() async { 6 | // init Supabase singleton 7 | // no localStorage provided, fallback to use hive as default 8 | await Supabase.initialize( 9 | url: supabaseUrl, 10 | anonKey: supabaseAnnonKey, 11 | authCallbackUrlHostname: 'login-callback', 12 | debug: true, 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import 'configure_nonweb.dart' if (dart.library.html) 'configure_web.dart'; 5 | import 'screens/change_password.dart'; 6 | import 'screens/forgot_password.dart'; 7 | import 'screens/profile_screen.dart'; 8 | import 'screens/signin_screen.dart'; 9 | import 'screens/signup_screen.dart'; 10 | import 'screens/splash_screen.dart'; 11 | import 'screens/web_home_screen.dart'; 12 | 13 | Future main() async { 14 | WidgetsFlutterBinding.ensureInitialized(); 15 | await configureApp(); 16 | runApp(MyApp()); 17 | } 18 | 19 | class MyApp extends StatelessWidget { 20 | // This widget is the root of your application. 21 | @override 22 | Widget build(BuildContext context) { 23 | return MaterialApp( 24 | title: 'Supabase Demo', 25 | theme: ThemeData.dark(), 26 | initialRoute: '/', 27 | routes: { 28 | '/signIn': (_) => SignInScreen(), 29 | '/signUp': (_) => SignUpScreen(), 30 | '/forgotPassword': (_) => ForgotPasswordScreen(), 31 | '/profile': (_) => ProfileScreen(), 32 | '/profile/changePassword': (_) => ChangePasswordScreen(), 33 | }, 34 | onGenerateRoute: generateRoute, 35 | ); 36 | } 37 | } 38 | 39 | Route generateRoute(RouteSettings settings) { 40 | switch (settings.name) { 41 | default: 42 | return MaterialPageRoute( 43 | builder: (_) => kIsWeb ? WebHomeScreen() : SplashScreen()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/screens/change_password.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:rounded_loading_button/rounded_loading_button.dart'; 3 | import 'package:supabase/supabase.dart'; 4 | import 'package:supabase_flutter/supabase_flutter.dart'; 5 | 6 | import '/components/auth_required_state.dart'; 7 | import '/utils/helpers.dart'; 8 | 9 | class ChangePasswordScreen extends StatefulWidget { 10 | @override 11 | _ChangePasswordState createState() => _ChangePasswordState(); 12 | } 13 | 14 | class _ChangePasswordState extends AuthRequiredState { 15 | final formKey = GlobalKey(); 16 | final scaffoldKey = GlobalKey(); 17 | 18 | final TextEditingController _passwordField = TextEditingController(); 19 | 20 | final RoundedLoadingButtonController _btnController = 21 | RoundedLoadingButtonController(); 22 | 23 | String _password = ''; 24 | 25 | Future _onPasswordChangePress(BuildContext context) async { 26 | try { 27 | final form = formKey.currentState; 28 | 29 | if (form != null && form.validate()) { 30 | form.save(); 31 | FocusScope.of(context).unfocus(); 32 | 33 | final userAttributes = UserAttributes(password: _password); 34 | final response = 35 | await Supabase.instance.client.auth.update(userAttributes); 36 | if (response.error != null) { 37 | throw 'Password change failed: ${response.error!.message}'; 38 | } 39 | 40 | showMessage('Password updated'); 41 | if (Navigator.canPop(context)) { 42 | _btnController.success(); 43 | } else { 44 | Navigator.pushNamedAndRemoveUntil( 45 | context, 46 | '/profile', 47 | (route) => false, 48 | ); 49 | } 50 | } 51 | } catch (e) { 52 | showMessage(e.toString()); 53 | } finally { 54 | _btnController.reset(); 55 | } 56 | } 57 | 58 | void showMessage(String message) { 59 | final snackbar = SnackBar(content: Text(message)); 60 | ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(snackbar); 61 | } 62 | 63 | @override 64 | Widget build(BuildContext context) { 65 | return Scaffold( 66 | key: scaffoldKey, 67 | appBar: AppBar( 68 | title: const Text('Change password'), 69 | ), 70 | body: Padding( 71 | padding: const EdgeInsets.all(15.0), 72 | child: Form( 73 | key: formKey, 74 | child: Column( 75 | children: [ 76 | const SizedBox(height: 25.0), 77 | TextFormField( 78 | onSaved: (value) => _password = value ?? '', 79 | validator: (val) => validatePassword(val), 80 | controller: _passwordField, 81 | obscureText: true, 82 | decoration: const InputDecoration( 83 | hintText: 'Password', 84 | ), 85 | ), 86 | const SizedBox(height: 15.0), 87 | TextFormField( 88 | validator: (val) => 89 | val != _passwordField.text ? 'Not Match' : null, 90 | obscureText: true, 91 | decoration: const InputDecoration( 92 | hintText: 'Confirm password', 93 | ), 94 | ), 95 | const SizedBox(height: 15.0), 96 | RoundedLoadingButton( 97 | color: Colors.green, 98 | controller: _btnController, 99 | onPressed: () { 100 | _onPasswordChangePress(context); 101 | }, 102 | child: const Text( 103 | 'Save', 104 | style: TextStyle(fontSize: 16, color: Colors.white), 105 | ), 106 | ), 107 | ], 108 | ), 109 | ), 110 | ), 111 | ); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /lib/screens/forgot_password.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:rounded_loading_button/rounded_loading_button.dart'; 3 | import 'package:supabase/supabase.dart' as supabase; 4 | import 'package:supabase_flutter/supabase_flutter.dart'; 5 | 6 | import '/components/auth_state.dart'; 7 | import '/utils/helpers.dart'; 8 | 9 | class ForgotPasswordScreen extends StatefulWidget { 10 | @override 11 | _ForgotPasswordState createState() => _ForgotPasswordState(); 12 | } 13 | 14 | class _ForgotPasswordState extends AuthState { 15 | final formKey = GlobalKey(); 16 | final scaffoldKey = GlobalKey(); 17 | 18 | final RoundedLoadingButtonController _btnController = 19 | RoundedLoadingButtonController(); 20 | 21 | String _email = ''; 22 | 23 | Future _onPasswordRecoverPress(BuildContext context) async { 24 | final form = formKey.currentState; 25 | 26 | if (form != null && form.validate()) { 27 | form.save(); 28 | FocusScope.of(context).unfocus(); 29 | 30 | final response = await Supabase.instance.client.auth.api 31 | .resetPasswordForEmail(_email, 32 | options: supabase.AuthOptions(redirectTo: authRedirectUri)); 33 | if (response.error != null) { 34 | showMessage('Password recovery failed: ${response.error!.message}'); 35 | _btnController.reset(); 36 | } else { 37 | showMessage('Please check your email for further instructions.'); 38 | _btnController.success(); 39 | } 40 | } 41 | } 42 | 43 | void showMessage(String message) { 44 | final snackbar = SnackBar(content: Text(message)); 45 | ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(snackbar); 46 | } 47 | 48 | @override 49 | Widget build(BuildContext context) { 50 | return Scaffold( 51 | key: scaffoldKey, 52 | appBar: AppBar( 53 | title: const Text('Forgot password'), 54 | ), 55 | body: Padding( 56 | padding: const EdgeInsets.all(15.0), 57 | child: Form( 58 | key: formKey, 59 | child: Column( 60 | children: [ 61 | const SizedBox(height: 25.0), 62 | TextFormField( 63 | onSaved: (value) => _email = value ?? '', 64 | validator: (val) => validateEmail(val), 65 | keyboardType: TextInputType.emailAddress, 66 | decoration: const InputDecoration( 67 | hintText: 'Enter your email address', 68 | ), 69 | ), 70 | const SizedBox(height: 35.0), 71 | RoundedLoadingButton( 72 | color: Colors.green, 73 | controller: _btnController, 74 | onPressed: () { 75 | _onPasswordRecoverPress(context); 76 | }, 77 | child: const Text( 78 | 'Send reset password instructions', 79 | style: TextStyle(fontSize: 16, color: Colors.white), 80 | ), 81 | ) 82 | ], 83 | ), 84 | ), 85 | ), 86 | ); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /lib/screens/profile_screen.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/foundation.dart'; 5 | import 'package:supabase/supabase.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:rounded_loading_button/rounded_loading_button.dart'; 8 | import 'package:image_picker/image_picker.dart'; 9 | import 'package:supabase_flutter/supabase_flutter.dart'; 10 | 11 | import '/components/auth_required_state.dart'; 12 | import '/utils/helpers.dart'; 13 | 14 | class ProfileScreen extends StatefulWidget { 15 | @override 16 | _ProfileScreenState createState() => _ProfileScreenState(); 17 | } 18 | 19 | class _ProfileScreenState extends AuthRequiredState { 20 | _ProfileScreenState(); 21 | 22 | final scaffoldKey = GlobalKey(); 23 | 24 | final RoundedLoadingButtonController _signOutBtnController = 25 | RoundedLoadingButtonController(); 26 | final RoundedLoadingButtonController _updateProfileBtnController = 27 | RoundedLoadingButtonController(); 28 | 29 | final _picker = ImagePicker(); 30 | 31 | User? user; 32 | bool loadingProfile = true; 33 | String _appBarTitle = ''; 34 | String username = ''; 35 | String website = ''; 36 | String avatarUrl = ''; 37 | String avatarKey = ''; 38 | 39 | @override 40 | void onAuthenticated(Session session) { 41 | final _user = session.user; 42 | if (_user != null) { 43 | setState(() { 44 | _appBarTitle = 'Welcome ${_user.email}'; 45 | user = _user; 46 | }); 47 | _loadProfile(_user.id); 48 | } 49 | } 50 | 51 | Future _loadProfile(String userId) async { 52 | try { 53 | final response = await Supabase.instance.client 54 | .from('profiles') 55 | .select('username, website, avatar_url, updated_at') 56 | .eq('id', userId) 57 | .maybeSingle() 58 | .execute(); 59 | if (response.error != null) { 60 | throw "Load profile failed: ${response.error!.message}"; 61 | } 62 | 63 | setState(() { 64 | print(response.data); 65 | username = response.data?['username'] as String? ?? ''; 66 | website = response.data?['website'] as String? ?? ''; 67 | avatarUrl = response.data?['avatar_url'] as String? ?? ''; 68 | final updatedAt = response.data?['updated_at'] as String? ?? ''; 69 | avatarKey = '$avatarUrl-$updatedAt'; 70 | }); 71 | } catch (e) { 72 | showMessage(e.toString()); 73 | } finally { 74 | setState(() { 75 | loadingProfile = false; 76 | }); 77 | } 78 | } 79 | 80 | Future _onSignOutPress(BuildContext context) async { 81 | await Supabase.instance.client.auth.signOut(); 82 | Navigator.pushNamedAndRemoveUntil(context, '/signIn', (route) => false); 83 | } 84 | 85 | Future _updateAvatar(BuildContext context) async { 86 | try { 87 | final pickedFile = await _picker.pickImage( 88 | source: ImageSource.gallery, 89 | maxHeight: 600, 90 | maxWidth: 600, 91 | ); 92 | if (pickedFile == null) { 93 | return; 94 | } 95 | 96 | final size = await pickedFile.length(); 97 | if (size > 1000000) { 98 | throw "The file is too large. Allowed maximum size is 1 MB."; 99 | } 100 | 101 | final bytes = await pickedFile.readAsBytes(); 102 | final fileName = avatarUrl == '' ? '${randomString(15)}.jpg' : avatarUrl; 103 | const fileOptions = FileOptions(upsert: true); 104 | final uploadRes = await Supabase.instance.client.storage 105 | .from('avatars') 106 | .uploadBinary(fileName, bytes, fileOptions: fileOptions); 107 | 108 | if (uploadRes.error != null) { 109 | throw uploadRes.error!.message; 110 | } 111 | 112 | final updatedAt = DateTime.now().toString(); 113 | final res = await Supabase.instance.client.from('profiles').upsert({ 114 | 'id': user!.id, 115 | 'avatar_url': fileName, 116 | 'updated_at': updatedAt, 117 | }).execute(); 118 | if (res.error != null) { 119 | throw res.error!.message; 120 | } 121 | 122 | setState(() { 123 | avatarUrl = fileName; 124 | avatarKey = '$fileName-$updatedAt'; 125 | }); 126 | showMessage("Avatar updated!"); 127 | } catch (e) { 128 | showMessage(e.toString()); 129 | } 130 | } 131 | 132 | Future _onUpdateProfilePress(BuildContext context) async { 133 | try { 134 | FocusScope.of(context).unfocus(); 135 | 136 | final updates = { 137 | 'id': user?.id, 138 | 'username': username, 139 | 'website': website, 140 | 'updated_at': DateTime.now().toString(), 141 | }; 142 | 143 | final response = await Supabase.instance.client 144 | .from('profiles') 145 | .upsert(updates) 146 | .execute(); 147 | if (response.error != null) { 148 | throw "Update profile failed: ${response.error!.message}"; 149 | } 150 | 151 | showMessage("Profile updated!"); 152 | } catch (e) { 153 | showMessage(e.toString()); 154 | } finally { 155 | _updateProfileBtnController.reset(); 156 | } 157 | } 158 | 159 | void showMessage(String message) { 160 | final snackbar = SnackBar(content: Text(message)); 161 | ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(snackbar); 162 | } 163 | 164 | @override 165 | Widget build(BuildContext context) { 166 | if (loadingProfile) { 167 | return Scaffold( 168 | appBar: AppBar( 169 | title: Text(_appBarTitle), 170 | ), 171 | body: SizedBox( 172 | height: MediaQuery.of(context).size.height / 1.3, 173 | child: const Center( 174 | child: CircularProgressIndicator(), 175 | ), 176 | ), 177 | ); 178 | } else { 179 | return Scaffold( 180 | key: scaffoldKey, 181 | resizeToAvoidBottomInset: false, 182 | appBar: AppBar( 183 | title: Text(_appBarTitle), 184 | ), 185 | body: Padding( 186 | padding: const EdgeInsets.all(15.0), 187 | child: Column( 188 | children: [ 189 | AvatarContainer( 190 | url: avatarUrl, 191 | onUpdatePressed: () => _updateAvatar(context), 192 | key: Key(avatarKey), 193 | ), 194 | TextFormField( 195 | onChanged: (value) => setState(() { 196 | username = value; 197 | }), 198 | initialValue: username, 199 | keyboardType: TextInputType.emailAddress, 200 | decoration: const InputDecoration( 201 | labelText: 'Username', 202 | hintText: '', 203 | ), 204 | ), 205 | TextFormField( 206 | onChanged: (value) => setState(() { 207 | website = value; 208 | }), 209 | initialValue: website, 210 | keyboardType: TextInputType.emailAddress, 211 | decoration: const InputDecoration( 212 | labelText: 'Website', 213 | hintText: '', 214 | ), 215 | ), 216 | const SizedBox( 217 | height: 35.0, 218 | ), 219 | RoundedLoadingButton( 220 | color: Colors.green, 221 | controller: _updateProfileBtnController, 222 | onPressed: () { 223 | _onUpdateProfilePress(context); 224 | }, 225 | child: const Text('Update profile', 226 | style: TextStyle(fontSize: 20, color: Colors.white)), 227 | ), 228 | const SizedBox(height: 15.0), 229 | TextButton( 230 | onPressed: () { 231 | stopAuthObserver(); 232 | Navigator.pushNamed(context, '/profile/changePassword') 233 | .then((_) => startAuthObserver()); 234 | }, 235 | child: const Text("Change password"), 236 | ), 237 | const Expanded(child: SizedBox()), 238 | RoundedLoadingButton( 239 | color: Colors.red, 240 | controller: _signOutBtnController, 241 | onPressed: () { 242 | _onSignOutPress(context); 243 | }, 244 | child: const Text('Sign out', 245 | style: TextStyle(fontSize: 20, color: Colors.white)), 246 | ), 247 | ], 248 | ), 249 | ), 250 | ); 251 | } 252 | } 253 | } 254 | 255 | class AvatarContainer extends StatefulWidget { 256 | final String url; 257 | final void Function() onUpdatePressed; 258 | const AvatarContainer( 259 | {required this.url, required this.onUpdatePressed, Key? key}) 260 | : super(key: key); 261 | 262 | @override 263 | _AvatarContainerState createState() => _AvatarContainerState(); 264 | } 265 | 266 | class _AvatarContainerState extends State { 267 | _AvatarContainerState(); 268 | 269 | bool loadingImage = false; 270 | Uint8List? image; 271 | 272 | @override 273 | void initState() { 274 | super.initState(); 275 | 276 | if (widget.url != '') { 277 | downloadImage(widget.url); 278 | } 279 | } 280 | 281 | Future downloadImage(String path) async { 282 | setState(() { 283 | loadingImage = true; 284 | }); 285 | 286 | final response = 287 | await Supabase.instance.client.storage.from('avatars').download(path); 288 | if (response.error == null) { 289 | setState(() { 290 | image = response.data; 291 | loadingImage = false; 292 | }); 293 | } else { 294 | print(response.error!.message); 295 | setState(() { 296 | loadingImage = false; 297 | }); 298 | } 299 | return true; 300 | } 301 | 302 | ImageProvider _getImage() { 303 | if (image != null) { 304 | return MemoryImage(image!); 305 | } else { 306 | return const AssetImage('assets/images/noavatar.jpeg'); 307 | } 308 | } 309 | 310 | @override 311 | Widget build(BuildContext context) { 312 | if (loadingImage) { 313 | return const CircleAvatar( 314 | radius: 65, 315 | child: Align( 316 | child: CircularProgressIndicator(), 317 | ), 318 | ); 319 | } else { 320 | return CircleAvatar( 321 | radius: 65, 322 | backgroundImage: _getImage(), 323 | child: Stack(children: [ 324 | Align( 325 | alignment: Alignment.bottomRight, 326 | child: IconButton( 327 | icon: const CircleAvatar( 328 | radius: 25, 329 | backgroundColor: Colors.white70, 330 | child: Icon( 331 | CupertinoIcons.camera, 332 | size: 18, 333 | ), 334 | ), 335 | onPressed: () => widget.onUpdatePressed(), 336 | ), 337 | ), 338 | ]), 339 | ); 340 | } 341 | } 342 | } 343 | -------------------------------------------------------------------------------- /lib/screens/signin_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:rounded_loading_button/rounded_loading_button.dart'; 3 | import 'package:supabase/supabase.dart' as supabase; 4 | import 'package:supabase_flutter/supabase_flutter.dart'; 5 | 6 | import '/components/auth_state.dart'; 7 | import '/utils/helpers.dart'; 8 | 9 | class SignInScreen extends StatefulWidget { 10 | @override 11 | _SignInState createState() => _SignInState(); 12 | } 13 | 14 | class _SignInState extends AuthState { 15 | final formKey = GlobalKey(); 16 | final scaffoldKey = GlobalKey(); 17 | 18 | final RoundedLoadingButtonController _signInEmailController = 19 | RoundedLoadingButtonController(); 20 | final RoundedLoadingButtonController _magicLinkController = 21 | RoundedLoadingButtonController(); 22 | final RoundedLoadingButtonController _githubSignInController = 23 | RoundedLoadingButtonController(); 24 | 25 | String _email = ''; 26 | String _password = ''; 27 | 28 | @override 29 | void onErrorAuthenticating(String message) { 30 | showMessage(message); 31 | _githubSignInController.reset(); 32 | } 33 | 34 | Future _onSignInPress(BuildContext context) async { 35 | final form = formKey.currentState; 36 | 37 | if (form != null && form.validate()) { 38 | form.save(); 39 | FocusScope.of(context).unfocus(); 40 | 41 | final response = await Supabase.instance.client.auth 42 | .signIn(email: _email, password: _password); 43 | if (response.error != null) { 44 | showMessage(response.error!.message); 45 | _signInEmailController.reset(); 46 | } else { 47 | Navigator.pushNamedAndRemoveUntil( 48 | context, 49 | '/profile', 50 | (route) => false, 51 | ); 52 | } 53 | } else { 54 | _signInEmailController.reset(); 55 | } 56 | } 57 | 58 | Future _onMagicLinkPress(BuildContext context) async { 59 | final form = formKey.currentState; 60 | 61 | if (form != null && form.validate()) { 62 | form.save(); 63 | FocusScope.of(context).unfocus(); 64 | 65 | final response = await Supabase.instance.client.auth.signIn( 66 | email: _email, 67 | options: supabase.AuthOptions( 68 | redirectTo: authRedirectUri, 69 | ), 70 | ); 71 | if (response.error != null) { 72 | showMessage(response.error!.message); 73 | _magicLinkController.reset(); 74 | } else { 75 | showMessage('Check your email for the login link!'); 76 | } 77 | } else { 78 | _magicLinkController.reset(); 79 | } 80 | } 81 | 82 | Future _githubSigninPressed(BuildContext context) async { 83 | FocusScope.of(context).unfocus(); 84 | 85 | Supabase.instance.client.auth.signInWithProvider( 86 | supabase.Provider.github, 87 | options: supabase.AuthOptions(redirectTo: authRedirectUri), 88 | ); 89 | } 90 | 91 | void showMessage(String message) { 92 | final snackbar = SnackBar(content: Text(message)); 93 | ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(snackbar); 94 | } 95 | 96 | @override 97 | Widget build(BuildContext context) { 98 | return Scaffold( 99 | key: scaffoldKey, 100 | resizeToAvoidBottomInset: false, 101 | appBar: AppBar( 102 | title: const Text('Sign in'), 103 | ), 104 | body: Padding( 105 | padding: const EdgeInsets.all(15.0), 106 | child: Form( 107 | key: formKey, 108 | child: Column( 109 | children: [ 110 | const SizedBox(height: 25.0), 111 | TextFormField( 112 | onSaved: (value) => _email = value ?? '', 113 | validator: (val) => validateEmail(val), 114 | keyboardType: TextInputType.emailAddress, 115 | decoration: const InputDecoration( 116 | hintText: 'Enter your email address', 117 | ), 118 | ), 119 | const SizedBox(height: 15.0), 120 | TextFormField( 121 | onSaved: (value) => _password = value ?? '', 122 | obscureText: true, 123 | decoration: const InputDecoration( 124 | hintText: 'Password', 125 | ), 126 | ), 127 | const SizedBox(height: 15.0), 128 | RoundedLoadingButton( 129 | color: Colors.green, 130 | controller: _signInEmailController, 131 | onPressed: () { 132 | _onSignInPress(context); 133 | }, 134 | child: const Text( 135 | 'Sign in', 136 | style: TextStyle(fontSize: 20, color: Colors.white), 137 | ), 138 | ), 139 | const SizedBox(height: 15.0), 140 | RoundedLoadingButton( 141 | color: Colors.green, 142 | controller: _magicLinkController, 143 | onPressed: () { 144 | _onMagicLinkPress(context); 145 | }, 146 | child: const Text( 147 | 'Send magic link', 148 | style: TextStyle(fontSize: 20, color: Colors.white), 149 | ), 150 | ), 151 | const SizedBox(height: 15.0), 152 | RoundedLoadingButton( 153 | color: Colors.black, 154 | controller: _githubSignInController, 155 | onPressed: () { 156 | _githubSigninPressed(context); 157 | }, 158 | child: const Text( 159 | 'Github Login', 160 | style: TextStyle(fontSize: 20, color: Colors.white), 161 | ), 162 | ), 163 | const SizedBox(height: 15.0), 164 | TextButton( 165 | onPressed: () { 166 | stopAuthObserver(); 167 | Navigator.pushNamed(context, '/forgotPassword') 168 | .then((_) => startAuthObserver()); 169 | }, 170 | child: const Text("Forgot your password ?"), 171 | ), 172 | TextButton( 173 | onPressed: () { 174 | stopAuthObserver(); 175 | Navigator.pushNamed(context, '/signUp') 176 | .then((_) => startAuthObserver()); 177 | }, 178 | child: const Text("Don’t have an Account ? Sign up"), 179 | ), 180 | ], 181 | ), 182 | ), 183 | ), 184 | ); 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /lib/screens/signup_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:rounded_loading_button/rounded_loading_button.dart'; 3 | import 'package:supabase/supabase.dart' as supabase; 4 | import 'package:supabase_flutter/supabase_flutter.dart'; 5 | 6 | import '/components/auth_state.dart'; 7 | import '/utils/helpers.dart'; 8 | 9 | class SignUpScreen extends StatefulWidget { 10 | @override 11 | _SignUpState createState() => _SignUpState(); 12 | } 13 | 14 | class _SignUpState extends AuthState { 15 | final formKey = GlobalKey(); 16 | final scaffoldKey = GlobalKey(); 17 | 18 | final RoundedLoadingButtonController _btnController = 19 | RoundedLoadingButtonController(); 20 | 21 | String _email = ''; 22 | String _password = ''; 23 | 24 | Future _onSignUpPress(BuildContext context) async { 25 | final form = formKey.currentState; 26 | 27 | if (form != null && form.validate()) { 28 | form.save(); 29 | FocusScope.of(context).unfocus(); 30 | 31 | final response = await Supabase.instance.client.auth.signUp( 32 | _email, _password, 33 | options: supabase.AuthOptions(redirectTo: authRedirectUri)); 34 | if (response.error != null) { 35 | showMessage('Sign up failed: ${response.error!.message}'); 36 | _btnController.reset(); 37 | } else if (response.data == null && response.user == null) { 38 | showMessage( 39 | "Please check your email and follow the instructions to verify your email address."); 40 | _btnController.success(); 41 | } else { 42 | Navigator.pushNamedAndRemoveUntil( 43 | context, 44 | '/profile', 45 | (route) => false, 46 | ); 47 | } 48 | } 49 | } 50 | 51 | void showMessage(String message) { 52 | final snackbar = SnackBar(content: Text(message)); 53 | ScaffoldMessenger.of(scaffoldKey.currentContext!).showSnackBar(snackbar); 54 | } 55 | 56 | @override 57 | Widget build(BuildContext context) { 58 | return Scaffold( 59 | key: scaffoldKey, 60 | appBar: AppBar( 61 | title: const Text('Sign up'), 62 | ), 63 | body: Padding( 64 | padding: const EdgeInsets.all(15.0), 65 | child: Form( 66 | key: formKey, 67 | child: Column( 68 | children: [ 69 | const SizedBox(height: 15.0), 70 | TextFormField( 71 | onSaved: (value) => _email = value ?? '', 72 | validator: (val) => validateEmail(val), 73 | keyboardType: TextInputType.emailAddress, 74 | decoration: const InputDecoration( 75 | hintText: 'Enter your email address', 76 | ), 77 | ), 78 | const SizedBox(height: 15.0), 79 | TextFormField( 80 | onSaved: (value) => _password = value ?? '', 81 | validator: (val) => validatePassword(val), 82 | obscureText: true, 83 | decoration: const InputDecoration( 84 | hintText: 'Password', 85 | ), 86 | ), 87 | const SizedBox(height: 15.0), 88 | RoundedLoadingButton( 89 | color: Colors.green, 90 | controller: _btnController, 91 | onPressed: () { 92 | _onSignUpPress(context); 93 | }, 94 | child: const Text( 95 | 'Sign up', 96 | style: TextStyle(fontSize: 20, color: Colors.white), 97 | ), 98 | ) 99 | ], 100 | ), 101 | ), 102 | ), 103 | ); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /lib/screens/splash_screen.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | import '/components/auth_state.dart'; 7 | 8 | class SplashScreen extends StatefulWidget { 9 | @override 10 | SplashScreenState createState() => SplashScreenState(); 11 | } 12 | 13 | class SplashScreenState extends AuthState 14 | with SingleTickerProviderStateMixin { 15 | Timer? recoverSessionTimer; 16 | 17 | @override 18 | void initState() { 19 | super.initState(); 20 | 21 | /// a timer to slow down session restore 22 | /// If not user can't really see the splash screen 23 | const _duration = Duration(seconds: 1); 24 | recoverSessionTimer = Timer(_duration, () { 25 | recoverSupabaseSession(); 26 | }); 27 | } 28 | 29 | /// on received auth deeplink, we should cancel recoverSessionTimer 30 | /// and wait for auth deep link handling result 31 | @override 32 | void onReceivedAuthDeeplink(Uri uri) { 33 | if (recoverSessionTimer != null) { 34 | recoverSessionTimer!.cancel(); 35 | } 36 | } 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | return Scaffold( 41 | body: Center( 42 | child: SizedBox( 43 | height: 50.0, 44 | child: Image.asset( 45 | "assets/images/logo-dark.png", 46 | ), 47 | ), 48 | ), 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/screens/web_home_screen.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:supabase_flutter/supabase_flutter.dart'; 6 | 7 | import '/components/auth_state.dart'; 8 | 9 | class WebHomeScreen extends StatefulWidget { 10 | @override 11 | _WebHomeScreenState createState() => _WebHomeScreenState(); 12 | } 13 | 14 | class _WebHomeScreenState extends AuthState 15 | with SingleTickerProviderStateMixin { 16 | @override 17 | void initState() { 18 | super.initState(); 19 | 20 | final uriParameters = SupabaseAuth.instance.parseUriParameters(Uri.base); 21 | if (uriParameters.containsKey('access_token') && 22 | uriParameters.containsKey('refresh_token') && 23 | uriParameters.containsKey('expires_in')) { 24 | /// Uri.base is a auth redirect link 25 | /// Call recoverSessionFromUrl to continue 26 | recoverSessionFromUrl(Uri.base); 27 | } 28 | } 29 | 30 | Future onSignIn() async { 31 | final hasAccessToken = await SupabaseAuth.instance.hasAccessToken; 32 | final String route = hasAccessToken ? '/profile' : '/signIn'; 33 | 34 | stopAuthObserver(); 35 | Navigator.pushNamed(context, route).then((_) => startAuthObserver()); 36 | } 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | return Scaffold( 41 | appBar: AppBar( 42 | title: const Text('Flutter user management'), 43 | ), 44 | body: Center( 45 | child: SizedBox( 46 | height: 50.0, 47 | child: ElevatedButton( 48 | onPressed: onSignIn, 49 | child: const Text('Sign in'), 50 | ), 51 | ), 52 | ), 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/utils/constants.dart: -------------------------------------------------------------------------------- 1 | /// TODO: update with your custom SCHEME and HOSTNAME 2 | const myAuthRedirectUri = 'io.supabase.flutterdemo://login-callback'; 3 | 4 | /// TODO: Add your SUPABASE_URL / SUPABASE_KEY here 5 | const supabaseUrl = 'SUPABASE_URL'; 6 | const supabaseAnnonKey = 'SUPABASE_KEY'; 7 | -------------------------------------------------------------------------------- /lib/utils/helpers.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | 5 | import '/utils/constants.dart'; 6 | 7 | String? validateEmail(String? value) { 8 | const String pattern = 9 | r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]" 10 | r"{0,253}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]" 11 | r"{0,253}[a-zA-Z0-9])?)*$"; 12 | final RegExp regex = RegExp(pattern); 13 | if (value == null || !regex.hasMatch(value)) { 14 | return 'Not a valid email.'; 15 | } else { 16 | return null; 17 | } 18 | } 19 | 20 | String? validatePassword(String? value) { 21 | return value == null || value.isEmpty ? 'Invalid password' : null; 22 | } 23 | 24 | String randomString(int length) { 25 | const ch = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz'; 26 | final Random r = Random(); 27 | return String.fromCharCodes( 28 | Iterable.generate(length, (_) => ch.codeUnitAt(r.nextInt(ch.length)))); 29 | } 30 | 31 | String? get authRedirectUri { 32 | if (kIsWeb) { 33 | return null; 34 | } else { 35 | return myAuthRedirectUri; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | archive: 5 | dependency: transitive 6 | description: 7 | name: archive 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "3.1.2" 11 | args: 12 | dependency: transitive 13 | description: 14 | name: args 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.0.0" 18 | async: 19 | dependency: transitive 20 | description: 21 | name: async 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "2.6.1" 25 | boolean_selector: 26 | dependency: transitive 27 | description: 28 | name: boolean_selector 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "2.1.0" 32 | characters: 33 | dependency: transitive 34 | description: 35 | name: characters 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.1.0" 39 | charcode: 40 | dependency: transitive 41 | description: 42 | name: charcode 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.2.0" 46 | clock: 47 | dependency: transitive 48 | description: 49 | name: clock 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.1.0" 53 | collection: 54 | dependency: transitive 55 | description: 56 | name: collection 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.15.0" 60 | cross_file: 61 | dependency: transitive 62 | description: 63 | name: cross_file 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "0.3.1+4" 67 | crypto: 68 | dependency: transitive 69 | description: 70 | name: crypto 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "3.0.1" 74 | cupertino_icons: 75 | dependency: "direct main" 76 | description: 77 | name: cupertino_icons 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "1.0.3" 81 | fake_async: 82 | dependency: transitive 83 | description: 84 | name: fake_async 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "1.2.0" 88 | ffi: 89 | dependency: transitive 90 | description: 91 | name: ffi 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "1.1.2" 95 | file: 96 | dependency: transitive 97 | description: 98 | name: file 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "6.1.2" 102 | flutter: 103 | dependency: "direct main" 104 | description: flutter 105 | source: sdk 106 | version: "0.0.0" 107 | flutter_launcher_icons: 108 | dependency: "direct dev" 109 | description: 110 | name: flutter_launcher_icons 111 | url: "https://pub.dartlang.org" 112 | source: hosted 113 | version: "0.9.0" 114 | flutter_plugin_android_lifecycle: 115 | dependency: transitive 116 | description: 117 | name: flutter_plugin_android_lifecycle 118 | url: "https://pub.dartlang.org" 119 | source: hosted 120 | version: "2.0.2" 121 | flutter_secure_storage: 122 | dependency: "direct main" 123 | description: 124 | name: flutter_secure_storage 125 | url: "https://pub.dartlang.org" 126 | source: hosted 127 | version: "4.2.0" 128 | flutter_test: 129 | dependency: "direct dev" 130 | description: flutter 131 | source: sdk 132 | version: "0.0.0" 133 | flutter_web_plugins: 134 | dependency: transitive 135 | description: flutter 136 | source: sdk 137 | version: "0.0.0" 138 | gotrue: 139 | dependency: transitive 140 | description: 141 | name: gotrue 142 | url: "https://pub.dartlang.org" 143 | source: hosted 144 | version: "0.1.3" 145 | hive: 146 | dependency: transitive 147 | description: 148 | name: hive 149 | url: "https://pub.dartlang.org" 150 | source: hosted 151 | version: "2.0.4" 152 | hive_flutter: 153 | dependency: transitive 154 | description: 155 | name: hive_flutter 156 | url: "https://pub.dartlang.org" 157 | source: hosted 158 | version: "1.1.0" 159 | http: 160 | dependency: transitive 161 | description: 162 | name: http 163 | url: "https://pub.dartlang.org" 164 | source: hosted 165 | version: "0.13.3" 166 | http_parser: 167 | dependency: transitive 168 | description: 169 | name: http_parser 170 | url: "https://pub.dartlang.org" 171 | source: hosted 172 | version: "4.0.0" 173 | image: 174 | dependency: transitive 175 | description: 176 | name: image 177 | url: "https://pub.dartlang.org" 178 | source: hosted 179 | version: "3.0.2" 180 | image_picker: 181 | dependency: "direct main" 182 | description: 183 | name: image_picker 184 | url: "https://pub.dartlang.org" 185 | source: hosted 186 | version: "0.8.2" 187 | image_picker_for_web: 188 | dependency: transitive 189 | description: 190 | name: image_picker_for_web 191 | url: "https://pub.dartlang.org" 192 | source: hosted 193 | version: "2.1.0" 194 | image_picker_platform_interface: 195 | dependency: transitive 196 | description: 197 | name: image_picker_platform_interface 198 | url: "https://pub.dartlang.org" 199 | source: hosted 200 | version: "2.2.0" 201 | js: 202 | dependency: transitive 203 | description: 204 | name: js 205 | url: "https://pub.dartlang.org" 206 | source: hosted 207 | version: "0.6.3" 208 | jwt_decode: 209 | dependency: transitive 210 | description: 211 | name: jwt_decode 212 | url: "https://pub.dartlang.org" 213 | source: hosted 214 | version: "0.3.1" 215 | lint: 216 | dependency: "direct dev" 217 | description: 218 | name: lint 219 | url: "https://pub.dartlang.org" 220 | source: hosted 221 | version: "1.5.3" 222 | matcher: 223 | dependency: transitive 224 | description: 225 | name: matcher 226 | url: "https://pub.dartlang.org" 227 | source: hosted 228 | version: "0.12.10" 229 | meta: 230 | dependency: transitive 231 | description: 232 | name: meta 233 | url: "https://pub.dartlang.org" 234 | source: hosted 235 | version: "1.3.0" 236 | mime: 237 | dependency: transitive 238 | description: 239 | name: mime 240 | url: "https://pub.dartlang.org" 241 | source: hosted 242 | version: "1.0.0" 243 | path: 244 | dependency: transitive 245 | description: 246 | name: path 247 | url: "https://pub.dartlang.org" 248 | source: hosted 249 | version: "1.8.0" 250 | path_provider: 251 | dependency: transitive 252 | description: 253 | name: path_provider 254 | url: "https://pub.dartlang.org" 255 | source: hosted 256 | version: "2.0.3" 257 | path_provider_linux: 258 | dependency: transitive 259 | description: 260 | name: path_provider_linux 261 | url: "https://pub.dartlang.org" 262 | source: hosted 263 | version: "2.0.0" 264 | path_provider_macos: 265 | dependency: transitive 266 | description: 267 | name: path_provider_macos 268 | url: "https://pub.dartlang.org" 269 | source: hosted 270 | version: "2.0.2" 271 | path_provider_platform_interface: 272 | dependency: transitive 273 | description: 274 | name: path_provider_platform_interface 275 | url: "https://pub.dartlang.org" 276 | source: hosted 277 | version: "2.0.1" 278 | path_provider_windows: 279 | dependency: transitive 280 | description: 281 | name: path_provider_windows 282 | url: "https://pub.dartlang.org" 283 | source: hosted 284 | version: "2.0.1" 285 | pedantic: 286 | dependency: transitive 287 | description: 288 | name: pedantic 289 | url: "https://pub.dartlang.org" 290 | source: hosted 291 | version: "1.11.1" 292 | petitparser: 293 | dependency: transitive 294 | description: 295 | name: petitparser 296 | url: "https://pub.dartlang.org" 297 | source: hosted 298 | version: "4.1.0" 299 | platform: 300 | dependency: transitive 301 | description: 302 | name: platform 303 | url: "https://pub.dartlang.org" 304 | source: hosted 305 | version: "3.0.0" 306 | plugin_platform_interface: 307 | dependency: transitive 308 | description: 309 | name: plugin_platform_interface 310 | url: "https://pub.dartlang.org" 311 | source: hosted 312 | version: "2.0.1" 313 | postgrest: 314 | dependency: transitive 315 | description: 316 | name: postgrest 317 | url: "https://pub.dartlang.org" 318 | source: hosted 319 | version: "0.1.9" 320 | process: 321 | dependency: transitive 322 | description: 323 | name: process 324 | url: "https://pub.dartlang.org" 325 | source: hosted 326 | version: "4.2.1" 327 | realtime_client: 328 | dependency: transitive 329 | description: 330 | name: realtime_client 331 | url: "https://pub.dartlang.org" 332 | source: hosted 333 | version: "0.1.13" 334 | rounded_loading_button: 335 | dependency: "direct main" 336 | description: 337 | name: rounded_loading_button 338 | url: "https://pub.dartlang.org" 339 | source: hosted 340 | version: "2.0.5" 341 | rxdart: 342 | dependency: transitive 343 | description: 344 | name: rxdart 345 | url: "https://pub.dartlang.org" 346 | source: hosted 347 | version: "0.26.0" 348 | sky_engine: 349 | dependency: transitive 350 | description: flutter 351 | source: sdk 352 | version: "0.0.99" 353 | source_span: 354 | dependency: transitive 355 | description: 356 | name: source_span 357 | url: "https://pub.dartlang.org" 358 | source: hosted 359 | version: "1.8.1" 360 | stack_trace: 361 | dependency: transitive 362 | description: 363 | name: stack_trace 364 | url: "https://pub.dartlang.org" 365 | source: hosted 366 | version: "1.10.0" 367 | storage_client: 368 | dependency: transitive 369 | description: 370 | name: storage_client 371 | url: "https://pub.dartlang.org" 372 | source: hosted 373 | version: "0.0.6+2" 374 | stream_channel: 375 | dependency: transitive 376 | description: 377 | name: stream_channel 378 | url: "https://pub.dartlang.org" 379 | source: hosted 380 | version: "2.1.0" 381 | string_scanner: 382 | dependency: transitive 383 | description: 384 | name: string_scanner 385 | url: "https://pub.dartlang.org" 386 | source: hosted 387 | version: "1.1.0" 388 | supabase: 389 | dependency: "direct main" 390 | description: 391 | name: supabase 392 | url: "https://pub.dartlang.org" 393 | source: hosted 394 | version: "0.2.12" 395 | supabase_flutter: 396 | dependency: "direct main" 397 | description: 398 | name: supabase_flutter 399 | url: "https://pub.dartlang.org" 400 | source: hosted 401 | version: "0.2.10" 402 | term_glyph: 403 | dependency: transitive 404 | description: 405 | name: term_glyph 406 | url: "https://pub.dartlang.org" 407 | source: hosted 408 | version: "1.2.0" 409 | test_api: 410 | dependency: transitive 411 | description: 412 | name: test_api 413 | url: "https://pub.dartlang.org" 414 | source: hosted 415 | version: "0.3.0" 416 | typed_data: 417 | dependency: transitive 418 | description: 419 | name: typed_data 420 | url: "https://pub.dartlang.org" 421 | source: hosted 422 | version: "1.3.0" 423 | uni_links: 424 | dependency: transitive 425 | description: 426 | name: uni_links 427 | url: "https://pub.dartlang.org" 428 | source: hosted 429 | version: "0.5.1" 430 | uni_links_platform_interface: 431 | dependency: transitive 432 | description: 433 | name: uni_links_platform_interface 434 | url: "https://pub.dartlang.org" 435 | source: hosted 436 | version: "1.0.0" 437 | uni_links_web: 438 | dependency: transitive 439 | description: 440 | name: uni_links_web 441 | url: "https://pub.dartlang.org" 442 | source: hosted 443 | version: "0.1.0" 444 | universal_io: 445 | dependency: transitive 446 | description: 447 | name: universal_io 448 | url: "https://pub.dartlang.org" 449 | source: hosted 450 | version: "2.0.4" 451 | url_launcher: 452 | dependency: transitive 453 | description: 454 | name: url_launcher 455 | url: "https://pub.dartlang.org" 456 | source: hosted 457 | version: "6.0.9" 458 | url_launcher_linux: 459 | dependency: transitive 460 | description: 461 | name: url_launcher_linux 462 | url: "https://pub.dartlang.org" 463 | source: hosted 464 | version: "2.0.0" 465 | url_launcher_macos: 466 | dependency: transitive 467 | description: 468 | name: url_launcher_macos 469 | url: "https://pub.dartlang.org" 470 | source: hosted 471 | version: "2.0.0" 472 | url_launcher_platform_interface: 473 | dependency: transitive 474 | description: 475 | name: url_launcher_platform_interface 476 | url: "https://pub.dartlang.org" 477 | source: hosted 478 | version: "2.0.4" 479 | url_launcher_web: 480 | dependency: transitive 481 | description: 482 | name: url_launcher_web 483 | url: "https://pub.dartlang.org" 484 | source: hosted 485 | version: "2.0.1" 486 | url_launcher_windows: 487 | dependency: transitive 488 | description: 489 | name: url_launcher_windows 490 | url: "https://pub.dartlang.org" 491 | source: hosted 492 | version: "2.0.0" 493 | vector_math: 494 | dependency: transitive 495 | description: 496 | name: vector_math 497 | url: "https://pub.dartlang.org" 498 | source: hosted 499 | version: "2.1.0" 500 | web_socket_channel: 501 | dependency: transitive 502 | description: 503 | name: web_socket_channel 504 | url: "https://pub.dartlang.org" 505 | source: hosted 506 | version: "2.1.0" 507 | win32: 508 | dependency: transitive 509 | description: 510 | name: win32 511 | url: "https://pub.dartlang.org" 512 | source: hosted 513 | version: "2.2.5" 514 | xdg_directories: 515 | dependency: transitive 516 | description: 517 | name: xdg_directories 518 | url: "https://pub.dartlang.org" 519 | source: hosted 520 | version: "0.2.0" 521 | xml: 522 | dependency: transitive 523 | description: 524 | name: xml 525 | url: "https://pub.dartlang.org" 526 | source: hosted 527 | version: "5.1.2" 528 | yaml: 529 | dependency: transitive 530 | description: 531 | name: yaml 532 | url: "https://pub.dartlang.org" 533 | source: hosted 534 | version: "3.1.0" 535 | sdks: 536 | dart: ">=2.13.0 <3.0.0" 537 | flutter: ">=2.0.0" 538 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: supabase_demo 2 | description: Supabase flutter user management. users can sign up with a magic link and then update their account with public profile information, including a profile image. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `pub publish`. This is preferred for private packages. 6 | publish_to: "none" # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 1.0.0+1 19 | 20 | environment: 21 | sdk: ">=2.12.0 <3.0.0" 22 | 23 | dependencies: 24 | cupertino_icons: ^1.0.3 25 | flutter: 26 | sdk: flutter 27 | flutter_secure_storage: ^4.2.0 28 | image_picker: ^0.8.2 29 | rounded_loading_button: ^2.0.5 30 | supabase: ^0.2.12 31 | supabase_flutter: ^0.2.10 32 | 33 | dev_dependencies: 34 | flutter_launcher_icons: ^0.9.0 35 | flutter_test: 36 | sdk: flutter 37 | lint: ^1.5.3 38 | 39 | flutter_icons: 40 | android: "launcher_icon" 41 | ios: true 42 | image_path: "assets/images/logo.png" 43 | 44 | # For information on the generic Dart part of this file, see the 45 | # following page: https://dart.dev/tools/pub/pubspec 46 | 47 | # The following section is specific to Flutter. 48 | flutter: 49 | # The following line ensures that the Material Icons font is 50 | # included with your application, so that you can use the icons in 51 | # the material Icons class. 52 | uses-material-design: true 53 | 54 | # To add assets to your application, add an assets section, like this: 55 | assets: 56 | - assets/images/ 57 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:supabase_demo/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/web/favicon.ico -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phamhieu/supabase-flutter-demo/a4a57ce2bd688806e5b0492fd73ec6f3bae6f3be/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | supabase_demo 27 | 28 | 29 | 30 | 33 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "supabase_demo", 3 | "short_name": "supabase_demo", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | } 22 | ] 23 | } 24 | --------------------------------------------------------------------------------