Flutter에서 Amplify Auth를 사용하는 방법
모 말라카 | 동기 부여 | 긍정적 인 영향
@mohammedmalaka
작업 & 재미있다
오후 16:51 - 2021년 1월 5일
이 게시물에서는 Auth 카테고리를 Flutter 앱에 통합하고 iOS에서 실행합니다.
증폭 설정
Amplify Admin UI를 사용하여 백엔드를 생성하겠습니다. 이전 게시물(아래 링크)에서 공유한 지침을 사용하여 이름(AuthFlutter)의 프로젝트를 만듭니다.
안녕하세요 @AWSAmplify, 이게 음식인가요?
오프라인 프로그래머 ・ 12월 15일 '20 ・ 9분 읽기
#codenewbie
#java
#android
#beginners
인증을 구성하고 배포합니다. 확인을 위해 (이메일)을 사용할 예정입니다.
프로젝트 설정
터미널을 사용하여 아래 명령을 실행하여 새 flutter 프로젝트를 만듭니다.
flutter create auth_for_flutter
다음으로 VS Code를 사용하여 프로젝트 폴더를 열겠습니다. 통합 터미널이 ios 폴더에 있는지 확인하십시오.
다음 명령을 실행하면 ios 폴더에 Podfile이 생성됩니다.
sudo gem install cocoapods
pod init
아래와 같이 Podile을 업데이트합니다.
platform :ios, '13.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__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end
XCode의 프로젝트 네비게이터에서 프로젝트를 선택하고 "정보"탭을 선택합니다. 배포 대상을 13.0 이상으로 설정합니다.
앱의 pubspec.yaml을 열고 "sdk:flutter"줄 아래에 다음 종속성을 추가합니다.
dependencies:
flutter:
sdk: flutter
amplify_core: '<1.0.0'
amplify_auth_cognito: '<1.0.0'
ios 폴더에서 아래 명령을 실행하십시오.
pod install
필요한 Pod를 설치합니다.
앱의 루트 폴더에 있는 Admin UI에서 풀 명령을 실행하여 위에서 생성한 Amplify 백엔드를 풀다운합니다.
프롬프트된 질문에 답하고 완료되면 아래와 같이 확인 메시지를 받게 됩니다.
Added backend environment config object to your project.
Run 'amplify pull' to sync upstream changes.
앱의 pubspec.yaml을 업데이트하여 사용할 이미지 파일을 추가합니다.
# To add assets to your application, add an assets section, like this:
assets:
- assets/images/applogo.png
앱을 실행하여 성공적으로 빌드되는지 확인합니다.
구현
인증 상태에 열거형을 사용할 것입니다. (lib) 폴더에 아래 파일을 생성합니다.
account_screens_enum.dart
enum AccountStatus {
sign_in,
sign_up,
reset_password,
confirm_code,
main_screen
}
다음으로 (lib\widgets) 폴더에 위젯을 생성해 보겠습니다.
sign_up.dart
계정을 만들려면.
import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_core/amplify_core.dart';
import 'package:auth_for_flutter/account_screens_enum.dart';
import 'package:auth_for_flutter/widgets/confirm_signup.dart';
import 'package:auth_for_flutter/widgets/error_view.dart';
import 'package:flutter/material.dart';
class SignUpView extends StatefulWidget {
final Function _displayAccountWidget;
const SignUpView(this._displayAccountWidget);
@override
_SignUpViewState createState() => _SignUpViewState();
}
class _SignUpViewState extends State<SignUpView> {
final emailController = TextEditingController();
final passwordController = TextEditingController();
bool _isSignedUp = false;
DateTime signupDate;
String _signUpError = "";
List<String> _signUpExceptions = [];
@override
void initState() {
super.initState();
}
void _setError(AuthError error) {
setState(() {
_signUpError = error.cause;
_signUpExceptions.clear();
error.exceptionList.forEach((el) {
_signUpExceptions.add(el.exception);
});
});
}
void _signUp(BuildContext context) async {
try {
Map<String, dynamic> userAttributes = {
"email": emailController.text.trim(),
"preferred_username": emailController.text.trim(),
// additional attributes as needed
};
SignUpResult res = await Amplify.Auth.signUp(
username: emailController.text.trim(),
password: passwordController.text.trim(),
options: CognitoSignUpOptions(userAttributes: userAttributes));
print(res.isSignUpComplete);
setState(() {
_isSignedUp = true;
});
} on AuthError catch (error) {
_setError(error);
}
}
@override
Widget build(BuildContext context) {
return Card(
elevation: 2,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
child: Padding(
padding: EdgeInsets.all(10.0),
child: Column(
children: [
Visibility(
visible: !_isSignedUp,
child: Column(children: [
TextFormField(
enableSuggestions: false,
decoration: const InputDecoration(
icon: Icon(Icons.email),
hintText: 'Email',
labelText: 'Email *',
),
controller: emailController,
keyboardType: TextInputType.emailAddress,
),
TextFormField(
obscureText: true,
enableSuggestions: false,
autocorrect: false,
decoration: const InputDecoration(
icon: Icon(Icons.lock),
hintText: 'Password',
labelText: 'Password *',
),
controller: passwordController,
),
const Padding(padding: EdgeInsets.all(10.0)),
FlatButton(
textColor:
Colors.black, // Theme.of(context).primaryColor,
color: Colors.amber,
onPressed: () => _signUp(context),
child: Text(
'Create Account',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
FlatButton(
height: 5,
onPressed: _displaySignIn,
child: Text(
'Already registered? Sign In',
style: Theme.of(context).textTheme.subtitle2,
),
),
]),
),
Visibility(
visible: _isSignedUp,
child: Column(children: [
ConfirmSignup(emailController.text.trim(), _setError),
])),
ErrorView(_signUpError, _signUpExceptions)
],
),
),
),
],
),
);
}
void _displaySignIn() {
widget._displayAccountWidget(AccountStatus.sign_in.index);
}
}
Confirm_signup.dart
가입 확인 코드를 제출합니다.
import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_core/amplify_core.dart';
import 'package:auth_for_flutter/screens/next_screen.dart';
import 'package:flutter/material.dart';
class ConfirmSignup extends StatelessWidget {
final codeController = TextEditingController();
final String userName;
final Function setError;
ConfirmSignup(this.userName, this.setError);
void _skip_confirm_signup(BuildContext context) {
_go_to_NextScreen(context);
}
void _confirm_signup(BuildContext context) async {
try {
SignUpResult res = await Amplify.Auth.confirmSignUp(
username: this.userName,
confirmationCode: codeController.text.trim());
_go_to_NextScreen(context);
} on AuthError catch (e) {
setError(e);
}
}
void _go_to_NextScreen(BuildContext context) {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (_) {
return NextScreen();
},
),
);
}
@override
Widget build(BuildContext context) {
return Container(
// decoration: BoxDecoration(borderRadius: BorderRadius.circular(20)),
padding: EdgeInsets.all(5),
child: Column(
children: [
TextFormField(
controller: codeController,
decoration: const InputDecoration(
icon: Icon(Icons.confirmation_number),
hintText: 'The code we sent you',
labelText: 'Confirmation Code *',
)),
FlatButton(
textColor: Colors.black, // Theme.of(context).primaryColor,
color: Colors.amber,
onPressed: () => _confirm_signup(context),
child: Text(
'Confirm Sign Up',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
],
),
);
}
}
reset_password.dart
암호 재설정 작업을 요청합니다.
import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_core/amplify_core.dart';
import 'package:auth_for_flutter/account_screens_enum.dart';
import 'package:auth_for_flutter/widgets/confirm_reset_password.dart';
import 'package:auth_for_flutter/widgets/error_view.dart';
import 'package:flutter/material.dart';
class ResetPasswordView extends StatefulWidget {
final Function _displayAccountWidget;
const ResetPasswordView(this._displayAccountWidget);
@override
_ResetPasswordViewState createState() => _ResetPasswordViewState();
}
class _ResetPasswordViewState extends State<ResetPasswordView> {
final emailController = TextEditingController();
bool _isPasswordReset = false;
String _signUpError = "";
List<String> _signUpExceptions = [];
@override
void initState() {
super.initState();
}
void _setError(AuthError error) {
setState(() {
_signUpError = error.cause;
_signUpExceptions.clear();
error.exceptionList.forEach((el) {
_signUpExceptions.add(el.exception);
});
});
}
void _resetPassword(BuildContext context) async {
try {
ResetPasswordResult res = await Amplify.Auth.resetPassword(
username: emailController.text.trim(),
);
setState(() {
_isPasswordReset = true;
});
} on AuthError catch (e) {
setState(() {
_signUpError = e.cause;
_signUpExceptions.clear();
e.exceptionList.forEach((el) {
_signUpExceptions.add(el.exception);
});
});
}
}
@override
Widget build(BuildContext context) {
return Card(
elevation: 2,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
child: Padding(
padding: EdgeInsets.all(10.0),
child: Column(
children: [
Visibility(
visible: !_isPasswordReset,
child: Column(children: [
TextFormField(
enableSuggestions: false,
decoration: const InputDecoration(
icon: Icon(Icons.email),
hintText: 'Email',
labelText: 'Email *',
),
controller: emailController,
keyboardType: TextInputType.emailAddress,
),
const Padding(padding: EdgeInsets.all(10.0)),
FlatButton(
textColor:
Colors.black, // Theme.of(context).primaryColor,
color: Colors.amber,
onPressed: () => _resetPassword(context),
child: Text(
'Reset Password',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
FlatButton(
height: 5,
onPressed: _displayCreateAccount,
child: Text(
'Create Account',
style: Theme.of(context).textTheme.subtitle2,
),
),
]),
),
Visibility(
visible: _isPasswordReset,
child: Column(children: [
ConfirmResetPassword(
emailController.text.trim(), _setError),
])),
ErrorView(_signUpError, _signUpExceptions)
],
),
),
),
],
),
);
}
void _displayCreateAccount() {
widget._displayAccountWidget(AccountStatus.sign_up.index);
}
}
Confirm_reset_password.dart
확인 코드와 새 비밀번호를 제출합니다.
import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_core/amplify_core.dart';
import 'package:auth_for_flutter/screens/next_screen.dart';
import 'package:flutter/material.dart';
class ConfirmResetPassword extends StatelessWidget {
final codeController = TextEditingController();
final emailController = TextEditingController();
final passwordController = TextEditingController();
final String userName;
final Function setError;
ConfirmResetPassword(this.userName, this.setError);
void _confirm_password_reset(BuildContext context) async {
try {
await Amplify.Auth.confirmPassword(
username: this.userName,
newPassword: passwordController.text.trim(),
confirmationCode: codeController.text.trim());
_go_to_NextScreen(context);
} on AuthError catch (e) {
setError(e);
}
}
void _go_to_NextScreen(BuildContext context) {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (_) {
return NextScreen();
},
),
);
}
@override
Widget build(BuildContext context) {
return Container(
// decoration: BoxDecoration(borderRadius: BorderRadius.circular(20)),
padding: EdgeInsets.all(5),
child: Column(
children: [
TextFormField(
enableSuggestions: false,
decoration: const InputDecoration(
icon: Icon(Icons.email),
hintText: 'Email',
labelText: 'Email *',
),
controller: emailController,
keyboardType: TextInputType.emailAddress,
),
TextFormField(
obscureText: true,
enableSuggestions: false,
autocorrect: false,
decoration: const InputDecoration(
icon: Icon(Icons.lock),
hintText: 'New Password',
labelText: 'New Password *',
),
controller: passwordController,
),
TextFormField(
controller: codeController,
decoration: const InputDecoration(
icon: Icon(Icons.confirmation_number),
hintText: 'The code we sent you',
labelText: 'Confirmation Code *',
)),
FlatButton(
textColor: Colors.black, // Theme.of(context).primaryColor,
color: Colors.amber,
onPressed: () => _confirm_password_reset(context),
child: Text(
'Reset Password & Sign In',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
],
),
);
}
}
sign_in.dart
로그인 작업
import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_core/amplify_core.dart';
import 'package:auth_for_flutter/account_screens_enum.dart';
import 'package:auth_for_flutter/screens/next_screen.dart';
import 'package:auth_for_flutter/widgets/error_view.dart';
import 'package:flutter/material.dart';
class SignInView extends StatefulWidget {
final Function _displayAccountWidget;
const SignInView(this._displayAccountWidget);
@override
_SignInViewState createState() => _SignInViewState();
}
class _SignInViewState extends State<SignInView> {
final emailController = TextEditingController();
final passwordController = TextEditingController();
String _signUpError = "";
List<String> _signUpExceptions = [];
@override
void initState() {
super.initState();
}
void _setError(AuthError error) {
setState(() {
_signUpError = error.cause;
_signUpExceptions.clear();
error.exceptionList.forEach((el) {
_signUpExceptions.add(el.exception);
});
});
}
void _signIn() async {
// Sign out before in case a user is already signed in
// If a user is already signed in - Amplify.Auth.signIn will throw an exception
try {
await Amplify.Auth.signOut();
} on AuthError catch (e) {
print(e);
}
try {
SignInResult res = await Amplify.Auth.signIn(
username: emailController.text.trim(),
password: passwordController.text.trim());
_go_to_NextScreen(context);
} on AuthError catch (e) {
setState(() {
_signUpError = e.cause;
_signUpExceptions.clear();
e.exceptionList.forEach((el) {
_signUpExceptions.add(el.exception);
});
});
}
}
void _go_to_NextScreen(BuildContext context) {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (_) {
return NextScreen();
},
),
);
}
@override
Widget build(BuildContext context) {
return Card(
elevation: 2,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
// wrap your Column in Expanded
child: Padding(
padding: EdgeInsets.all(10.0),
child: Column(
children: [
TextFormField(
controller: emailController,
decoration: const InputDecoration(
icon: Icon(Icons.email),
hintText: 'Enter your email',
labelText: 'Email *',
),
),
TextFormField(
obscureText: true,
controller: passwordController,
decoration: const InputDecoration(
icon: Icon(Icons.lock),
hintText: 'Enter your password',
labelText: 'Password *',
),
),
const Padding(padding: EdgeInsets.all(10.0)),
FlatButton(
textColor: Colors.black, // Theme.of(context).primaryColor,
color: Colors.amber,
onPressed: _signIn,
child: const Text(
'Sign In',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
FlatButton(
height: 5,
onPressed: _displayCreateAccount,
child: Text(
'Create Account',
style: Theme.of(context).textTheme.subtitle2,
),
),
FlatButton(
height: 5,
onPressed: _displayResetPassword,
child: Text(
'Reset Password',
style: Theme.of(context).textTheme.subtitle2,
),
),
],
),
ErrorView(_signUpError, _signUpExceptions)
],
),
),
),
],
),
);
}
void _displayCreateAccount() {
widget._displayAccountWidget(AccountStatus.sign_up.index);
}
void _displayResetPassword() {
widget._displayAccountWidget(AccountStatus.reset_password.index);
}
}
error_view.dart
오류 메시지를 표시합니다.
import 'package:flutter/material.dart';
class ErrorView extends StatelessWidget {
final String error;
final List<String> exceptions;
ErrorView(this.error, this.exceptions);
@override
Widget build(BuildContext context) {
// We do not recognize your username and/or password. Please try again.
if (error.isNotEmpty || exceptions.length > 0) {
return Column(children: <Widget>[
Text('Error: $error',
textAlign: TextAlign.center,
overflow: TextOverflow.visible,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Theme.of(context).errorColor,
)),
if (exceptions.length > 0) ...[_showExceptions(context)]
]);
} else {
return Container();
}
}
_showExceptions(context) {
return Column(
children: exceptions
.map((item) => new Text(item + " ",
style: TextStyle(
fontWeight: FontWeight.bold,
color: Theme.of(context).errorColor,
)))
.toList());
}
}
앱에는 세 개의 화면이 있습니다. (lib\screens) 폴더에 아래 화면을 생성합니다.
loading_screen.dart
import 'package:flutter/material.dart';
class LoadingScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('BatMan App')),
body: Container(
color: Color(0xff90CCE6),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
color: Color(0xffE1E5E4),
height: 200,
child: Image.asset(
'assets/images/applogo.png',
fit: BoxFit.cover,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: CircularProgressIndicator(),
),
),
],
),
),
),
);
}
}
main_screen.dart
import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_core/amplify_core.dart';
import 'package:auth_for_flutter/account_screens_enum.dart';
import 'package:auth_for_flutter/screens/next_screen.dart';
import 'package:auth_for_flutter/widgets/reset_password.dart';
import 'package:auth_for_flutter/widgets/sign_in.dart';
import 'package:auth_for_flutter/widgets/sign_up.dart';
import 'package:flutter/material.dart';
class MainScreen extends StatefulWidget {
@override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
var _accountWidget;
@override
initState() {
super.initState();
_fetchSession();
}
void _fetchSession() async {
// Sign out before in case a user is already signed in
try {
await Amplify.Auth.signOut();
} on AuthError catch (e) {
print(e);
}
_accountWidget = AccountStatus.sign_in.index;
_displayAccountWidget(_accountWidget);
}
void _go_to_NextScreen(BuildContext context) {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (_) {
return NextScreen();
},
),
);
}
void _displayAccountWidget(int accountStatus) {
setState(() {
_accountWidget = AccountStatus.values[accountStatus];
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('BatMan App'),
),
body: Container(
color: Color(0xffE1E5E4),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
color: Color(0xffE1E5E4),
height: 200,
child: Image.asset(
'assets/images/applogo.png',
fit: BoxFit.cover,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(children: [
Visibility(
visible: _accountWidget == AccountStatus.sign_in,
child: SignInView(_displayAccountWidget),
),
Visibility(
visible: _accountWidget == AccountStatus.sign_up,
child: SignUpView(_displayAccountWidget),
),
Visibility(
visible: _accountWidget == AccountStatus.reset_password,
child: ResetPasswordView(_displayAccountWidget),
),
Visibility(
visible: _accountWidget == AccountStatus.main_screen,
child: NextScreen(),
),
]),
),
],
),
),
),
);
}
}
next_screen.dart
import 'package:auth_for_flutter/screens/main_screen.dart';
import 'package:flutter/material.dart';
class NextScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('BatMan App')),
body: Container(
color: Color(0xffE1E5E4),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
color: Color(0xffE1E5E4),
height: 200,
child: Image.asset(
'assets/images/applogo.png',
fit: BoxFit.cover,
),
),
Padding(
padding: const EdgeInsets.all(20.0),
child: Center(
child: Text(
'I\'m BATMAN',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 30),
),
),
),
Padding(
padding: const EdgeInsets.all(20.0),
child: Center(
child: RaisedButton(
color: Colors.lightBlue,
onPressed: () => _signOut(context),
child: Text(
'Sign Out',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
),
),
),
],
),
),
),
);
}
void _signOut(BuildContext context) {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (_) {
return MainScreen();
},
),
);
}
}
앱 실행
코드 확인here
Follow me on for more tips about #coding, #learning, #technology...etc.
Check my Apps on Google Play
Cover image Obi Onyeador on Unsplash
Reference
이 문제에 관하여(Flutter에서 Amplify Auth를 사용하는 방법), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/offlineprogrammer/how-i-use-amplify-auth-with-flutter-1ndn텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)