Skip to content
This repository has been archived by the owner on Dec 7, 2024. It is now read-only.

Commit

Permalink
feat: supabase 연결
Browse files Browse the repository at this point in the history
  • Loading branch information
suu3 committed Jul 10, 2024
1 parent 4ba904f commit 533ac1d
Show file tree
Hide file tree
Showing 21 changed files with 834 additions and 66 deletions.
4 changes: 0 additions & 4 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ on:
jobs:
build:
name: Build Web
env:
my_secret: $
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
Expand All @@ -18,8 +16,6 @@ jobs:
- run: flutter config --enable-web
- run: flutter pub get
- run: flutter build web --release
# 1. change path which exist lib/main.dart
# working-directory: ./flutter-riverpod
- run: |
# 2. change path to [existed lib/main.dart path]/build/web
cd build/web
Expand Down
12 changes: 12 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>

<!-- Supabase -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- Accepts URIs that begin with YOUR_SCHEME://YOUR_HOST -->
<data
android:scheme="io.supabase.flutterquickstart"
android:host="login-callback" />
</intent-filter>

</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
Expand Down
1 change: 1 addition & 0 deletions ios/Flutter/Debug.xcconfig
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
1 change: 1 addition & 0 deletions ios/Flutter/Release.xcconfig
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
44 changes: 44 additions & 0 deletions ios/Podfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '12.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}

def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end

File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end

require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)

flutter_ios_podfile_setup

target 'Runner' do
use_frameworks!
use_modular_headers!

flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end

post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end
11 changes: 11 additions & 0 deletions ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>io.supabase.flutterquickstart</string>
</array>
</dict>
</array>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
Expand Down
1 change: 1 addition & 0 deletions lib/constants/routes.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
class Routes {
static const String login = '/login';
static const String home = '/home';
static const String profile = '/profile';
}
41 changes: 40 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,39 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod_demo/constants/routes.dart';
import 'package:flutter_riverpod_demo/screens/profile.dart';
import 'package:flutter_riverpod_demo/screens/home.dart';
import 'package:flutter_riverpod_demo/screens/login.dart';
import 'package:go_router/go_router.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

void main() {
void main() async {
WidgetsFlutterBinding.ensureInitialized();
//supabase
await Supabase.initialize(
url: 'https://nocblwmucgpaoopdhssr.supabase.co',
anonKey:
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im5vY2Jsd211Y2dwYW9vcGRoc3NyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTkzNzU4MjQsImV4cCI6MjAzNDk1MTgyNH0.vJoGmR_MUD4rXdJiIlow3yFDnNXYHAAxkmaU8sU4SpA',
);
runApp(const ProviderScope(child: MyApp()));
}

final supabase = Supabase.instance.client;

GoRouter router() {
return GoRouter(
initialLocation: Routes.login,
redirect: (context, state) {
final session = Supabase.instance.client.auth.currentSession;
final loggingIn = state.uri.toString() == Routes.login;

if (session == null && !loggingIn) {
return Routes.login;
} else if (session != null && loggingIn) {
return Routes.home;
}
return null;
},
routes: [
GoRoute(
path: Routes.login,
Expand All @@ -21,6 +43,10 @@ GoRouter router() {
path: Routes.home,
builder: (context, state) => const MyHome(),
),
GoRoute(
path: Routes.profile,
builder: (context, state) => const MyProfile(),
),
],
);
}
Expand Down Expand Up @@ -48,3 +74,16 @@ class MyApp extends StatelessWidget {
);
}
}

extension ContextExtension on BuildContext {
void showSnackBar(String message, {bool isError = false}) {
ScaffoldMessenger.of(this).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: isError
? Theme.of(this).colorScheme.error
: Theme.of(this).snackBarTheme.backgroundColor,
),
);
}
}
19 changes: 19 additions & 0 deletions lib/screens/home.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_riverpod_demo/constants/routes.dart';
import 'package:flutter_riverpod_demo/constants/todo_filter.dart';
import 'package:flutter_riverpod_demo/providers/task_provider.dart';
import 'package:flutter_riverpod_demo/widgets/add_task_bottomsheet.dart';
import 'package:flutter_riverpod_demo/widgets/empty_task_list.dart';
import 'package:flutter_riverpod_demo/widgets/task_list.dart';
import 'package:go_router/go_router.dart';

class MyHome extends ConsumerStatefulWidget {
const MyHome({super.key});
Expand All @@ -15,6 +17,21 @@ class MyHome extends ConsumerStatefulWidget {

class _MyHomeState extends ConsumerState<MyHome> {
int _selectedFilterIndex = filterAll.index;
int _selectedBottomNavIndex = 0;

void _onItemTapped(int index) {
setState(() {
_selectedBottomNavIndex = index;
});
switch (index) {
case 0:
context.go(Routes.home);
break;
case 1:
context.go(Routes.profile);
break;
}
}

List<Task> _filterTasks(List<Task> tasks) {
List<Task> filteredTasks;
Expand Down Expand Up @@ -148,6 +165,8 @@ class _MyHomeState extends ConsumerState<MyHome> {
backgroundColor: Colors.grey,
selectedItemColor: Colors.white,
unselectedItemColor: Colors.grey[20],
currentIndex: _selectedBottomNavIndex,
onTap: _onItemTapped,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
Expand Down
154 changes: 94 additions & 60 deletions lib/screens/login.dart
Original file line number Diff line number Diff line change
@@ -1,70 +1,104 @@
import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod_demo/constants/routes.dart';
import 'package:go_router/go_router.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:flutter_riverpod_demo/main.dart';
// import 'package:flutter_riverpod_demo/screens/profile.dart';

class MyLogin extends StatelessWidget {
class MyLogin extends StatefulWidget {
const MyLogin({super.key});

@override
State<MyLogin> createState() => _MyLoginState();
}

class _MyLoginState extends State<MyLogin> {
bool _isLoading = false;
final bool _redirecting = false;
late final TextEditingController _emailController = TextEditingController();
late final StreamSubscription<AuthState> _authStateSubscription;

Future<void> _signIn() async {
try {
setState(() {
_isLoading = true;
});
await supabase.auth.signInWithOtp(
email: _emailController.text.trim(),
emailRedirectTo:
kIsWeb ? null : 'io.supabase.flutterquickstart://login-callback/',
);
if (mounted) {
context.showSnackBar('Check your email for a login link!');

_emailController.clear();
}
} on AuthException catch (error) {
if (mounted) context.showSnackBar(error.message, isError: true);
} catch (error) {
if (mounted) {
context.showSnackBar('Unexpected error occurred', isError: true);
}
} finally {
if (mounted) {
setState(() {
_isLoading = false;
});
}
}
}

// @override
// void initState() {
// _authStateSubscription = supabase.auth.onAuthStateChange.listen(
// (data) {
// if (_redirecting) return;
// final session = data.session;
// if (session != null) {
// _redirecting = true;
// Navigator.of(context).pushReplacement(
// MaterialPageRoute(builder: (context) => const MyProfile()),
// );
// }
// },
// onError: (error) {
// if (error is AuthException) {
// context.showSnackBar(error.message, isError: true);
// } else {
// context.showSnackBar('Unexpected error occurred', isError: true);
// }
// },
// );
// super.initState();
// }

@override
void dispose() {
_emailController.dispose();
_authStateSubscription.cancel();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(32),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(height: 120),
const Text(
'로그인',
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 32),
const TextField(
decoration: InputDecoration(
labelText: '이메일',
hintText: '이메일을 입력하세요.',
),
),
const SizedBox(height: 16),
const TextField(
obscureText: true,
decoration: InputDecoration(
labelText: '비밀번호',
hintText: '비밀번호를 입력하세요.',
),
),
const SizedBox(height: 32),
Row(
children: [
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
padding: const EdgeInsets.all(20)),
onPressed: () {
context.go(Routes.home);
},
child: const Text(
'로그인',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
),
],
),
const SizedBox(height: 16),
],
),
appBar: AppBar(title: const Text('Sign In')),
body: ListView(
padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 12),
children: [
const Text('Sign in via the magic link with your email below'),
const SizedBox(height: 18),
TextFormField(
controller: _emailController,
decoration: const InputDecoration(labelText: 'Email'),
),
const SizedBox(height: 18),
ElevatedButton(
onPressed: _isLoading ? null : _signIn,
child: Text(_isLoading ? 'Sending...' : 'Send Magic Link'),
),
],
),
);
}
Expand Down
Loading

0 comments on commit 533ac1d

Please sign in to comment.