-
- Getx AuthController 강의를 보고 따라서 제작하시오.
- AuthController에는 User의 정보만을 담고있다. 로그인을 하면 유저를 식별할 수 있는 Token 값도 함께 받아볼 수 있는데, 해당 Token 값을 AuthController 내에 저장할 수 있도록 하고, 코드를 제시하시오.
- 해당 API의 정보는 다음과 같다
- API URL
- // <http://52.79.115.43:8090/api/collections/users/auth-with-password>
- API Request
- // Method : POST // data : identity(String), password(String) // Teddy/sfac12341234
- API Response
- 해당 API의 정보는 다음과 같다
{ "token": "JWT_TOKEN", "record": { "id": "RECORD_ID", "collectionId": "_pb_users_auth_", "collectionName": "users", "created": "2022-01-01 01:00:00Z", "updated": "2022-01-01 23:59:59Z", "username": "username123", "verified": false, "emailVisibility": true, "email": "test@example.com", "name": "test", "avatar": "filename.jpg" } }
- MainController에는 readDocuments라는 멤버 함수(메서드)를 제작하시오.
- 해당 API의 정보는 다음과 같다 이 때, AuthController를 find하여 Token값이 존재하면 (로그인 되었다면) 실행할 수 있도록 한다.
- API URL
- // <http://52.79.115.43:8090/api/collections/documents/records>
- API Request (필수)
- // Method: GET
// 해당 API는 인증된 사용자만 사용할 수 있기 때문에
// 로그인 시 획득한 Token을 반드시 Request 헤더에 Authorization을 포함시켜야만합니다.
- API Response (성공시)
- 해당 API의 정보는 다음과 같다 이 때, AuthController를 find하여 Token값이 존재하면 (로그인 되었다면) 실행할 수 있도록 한다.
- 위 API 정보를 토대로 응답 데이터형식에 맞게 Document 커스텀 클래스를 제작하고, MainPage의 Home이 다음과 같이 출력되도록 한다.
- 아래 FAB를 누르면 readDocuments를 실행하고 결과를 화면에 출력한다.
- document리스트는 MainController 멤버변수로 저장한다.
- 다음의 제공되는 코드를 사용할 수 있도록 한다.
- /lib/model/document.dart
// ignore_for_file: public_member_api_docs, sort_constructors_first, non_constant_identifier_names class Document { String title; String content; String sec_level; String? attachment_url; Document({ required this.title, required this.content, required this.sec_level, this.attachment_url, }); factory Document.fromMap(Map<String, dynamic> map) { return Document( title: map['title'] as String, content: map['content'] as String, sec_level: map['sec_level'] as String, attachment_url: map['attachment_url'] != '' ? map['attachment_url'] : null, ); } }
lib/controller/auth_controller.dart
import 'package:dio/dio.dart'; import 'package:get/get.dart'; import 'package:myapp32/model/user.dart'; import 'package:myapp32/util/api_routes.dart'; import 'package:myapp32/util/app_routes.dart'; class AuthController extends GetxController { final Rxn<User> _user = Rxn(); Dio dio = Dio(); String? _token; User? get user => _user.value; //user를 불러오면 _user의 value를 전달해 String? get token => _token; //토큰 전달 login(String id, String pw) async { try { var res = await dio.post( 'http://52.79.115.43:8090${ApiRoutes.authWithPassword}', data: {'identity': id, 'password': pw}); //데이터따로관리해준 if (res.statusCode == 200) { var user = User.fromMap(res.data['record']); _user(user); //유저정보 저장 _token = res.data['token']; //토큰저장 } } on DioError catch (e) { print(e.message); } } logout() { _user.value = null; } _handleAuthChanged(User? data) { if (data != null) { //메인페이지 이동 Get.toNamed(AppRoutes.main); return; } //로그인페이지 이동 Get.toNamed(AppRoutes.login); return; } @override void onInit() { super.onInit(); ever(_user, _handleAuthChanged); } }
token...어케하지 눈치것 썼는데..
lib/controller/login_controller.dart
import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:myapp32/controller/auth_controller.dart'; class Logincontroller extends GetxController { var idController = TextEditingController(); var pwController = TextEditingController(); login() { Get.find<AuthController>().login(idController.text, pwController.text); //Authcontroller는 전역에서 사용가능하기 때문에 find할수있게됨 } }
lib/controller/main_controller.dart
import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:myapp32/controller/auth_controller.dart'; import 'package:myapp32/model/document.dart'; import 'package:myapp32/util/api_routes.dart'; class MainController extends GetxController { var pageController = PageController(); RxInt curPage = 0.obs; RxList<Document> documents = RxList(); Dio dio = Dio(); onPageTapped(int v) { pageController.jumpToPage(v); curPage(v); } logout() { Get.find<AuthController>().logout(); } readDocuments() async { dio.options.baseUrl = 'http://52.79.115.43:8090'; var token = Get.find<AuthController>().token; try { documents.clear(); var res = await dio.get(ApiRoutes.documents, options: Options(headers: {'authorization': token}), data: {token: token}); if (res.statusCode == 200) { List<Map<String, dynamic>> data = List<Map<String, dynamic>>.from(res.data['items']); documents.addAll(data.map((e) => Document.fromMap(e)).toList().obs); } } on DioError catch (e) { print(e.message); } } }
lib/model/document.dart 고대로 사용함
// ignore_for_file: public_member_api_docs, sort_constructors_first, non_constant_identifier_names class Document { String title; String content; String sec_level; String? attachment_url; Document({ required this.title, required this.content, required this.sec_level, this.attachment_url, }); factory Document.fromMap(Map<String, dynamic> map) { return Document( title: map['title'] as String, content: map['content'] as String, sec_level: map['sec_level'] as String, attachment_url: map['attachment_url'] != '' ? map['attachment_url'] : null, ); } }
lib/model/user.dart
// ignore_for_file: public_member_api_docs, sort_constructors_first, non_constant_identifier_names class Document { String title; String content; String sec_level; String? attachment_url; Document({ required this.title, required this.content, required this.sec_level, this.attachment_url, }); factory Document.fromMap(Map<String, dynamic> map) { return Document( title: map['title'] as String, content: map['content'] as String, sec_level: map['sec_level'] as String, attachment_url: map['attachment_url'] != '' ? map['attachment_url'] : null, ); } }
lib/util/api_routes.dart
//Api가 계속 늘어날수도 있기때문에 따로 관리해줌 class ApiRoutes { static const String authWithPassword = '/api/collections/users/auth-with-password'; static const String documents = '/api/collections/documents/records'; }
lib/util/app_routes.dart
import 'package:myapp32/view/login_page.dart'; import 'package:myapp32/view/main_page.dart'; class AppRoutes { static const main = MainPage.route; //메인 페이지 라우트 static const login = LoginPage.route; //로그인 페이지 라우트 }
lib/util/apppages.dart
import 'package:get/get.dart'; import 'package:myapp32/util/app_routes.dart'; import 'package:myapp32/view/login_page.dart'; import 'package:myapp32/view/main_page.dart'; class AppPages { static final pages = [ GetPage(name: AppRoutes.main, page: () => const MainPage()), GetPage(name: AppRoutes.login, page: () => const LoginPage()), ]; }
lib/view/main_page.dart
// ignore_for_file: prefer_const_constructors import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:myapp32/controller/auth_controller.dart'; import 'package:myapp32/controller/main_controller.dart'; import 'package:myapp32/view/homescreen.dart'; import 'package:myapp32/view/myscreen.dart'; class MainPage extends GetView<MainController> { const MainPage({super.key}); static const String route = '/main'; @override Widget build(BuildContext context) { var user = Get.find<AuthController>().user; return Scaffold( body: SafeArea( child: PageView( controller: controller.pageController, children: [ //Home페이지 Obx(() => HomeScreen(document: controller.documents.toList(), user: user!)), //My페이지 MyScreen(user: user!, onTap: controller.logout) ], )), bottomNavigationBar: Obx( () => BottomNavigationBar( currentIndex: controller.curPage.value, onTap: controller.onPageTapped, items: [ BottomNavigationBarItem( icon: Icon( Icons.home, ), label: 'Home'), BottomNavigationBarItem( icon: Icon( Icons.person, ), label: 'My') ]), ), floatingActionButton: Obx((() { if (controller.curPage.value == 0) { return FloatingActionButton( onPressed: controller.readDocuments, child: Icon(Icons.refresh), ); } return SizedBox(); })), ); } }
lib/view/login_page.dart
import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:myapp32/controller/login_controller.dart'; class LoginPage extends GetView<Logincontroller> { const LoginPage({super.key}); static const String route = '/login'; @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ TextField( controller: controller.idController, ), TextField( controller: controller.pwController, ), ElevatedButton(onPressed: controller.login, child: Text('로그인하기')) ], ), ), ); } }
lib/view/homescreen
import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:myapp32/controller/login_controller.dart'; class LoginPage extends GetView<Logincontroller> { const LoginPage({super.key}); static const String route = '/login'; @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ TextField( controller: controller.idController, ), TextField( controller: controller.pwController, ), ElevatedButton(onPressed: controller.login, child: Text('로그인하기')) ], ), ), ); } }
lib/view/myscreen
import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/container.dart'; import 'package:flutter/src/widgets/framework.dart'; import 'package:get/get.dart'; import 'package:myapp32/controller/auth_controller.dart'; import 'package:myapp32/model/user.dart'; class MyScreen extends StatelessWidget { const MyScreen({ super.key, required this.user, required this.onTap, }); final User user; final VoidCallback onTap; //입력 인자가 없는 함수를 호출할 수 있는 타입 @override Widget build(BuildContext context) { var user = Get.find<AuthController>().user!; return Scaffold( body: //My페이지 Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ ListTile( title: Text(user.username), subtitle: Text(user.name), ), ListTile( title: Text('로그아웃'), leading: Icon(Icons.logout), onTap: onTap, ), ])); } }
main.dart
// ignore_for_file: prefer_const_constructors import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:myapp32/controller/auth_controller.dart'; import 'package:myapp32/controller/login_controller.dart'; import 'package:myapp32/controller/main_controller.dart'; import 'package:myapp32/util/app_routes.dart'; import 'package:myapp32/util/apppages.dart'; import 'package:myapp32/view/login_page.dart'; import 'package:myapp32/view/main_page.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return GetMaterialApp( initialBinding: BindingsBuilder(() { Get.put(AuthController()); //initialBinding이 실행되면서 Get.put이실행됨 Get.lazyPut(() => Logincontroller()); Get.lazyPut( () => MainController(), ); }), getPages: AppPages.pages, home: Scaffold( body: Center( child: TextButton( onPressed: () => Get.toNamed(AppRoutes.login), child: Text('로그인페이지로 가실?'), ), ), ), ); } }
결과가 나왔당 어려운데 할만한거 같기도 하고...
내용정리를 아직못해서 시간이 더 걸릴거다..