33일차 과제 - rlatkddbs99/Flutter GitHub Wiki
오늘 삽질
-
url복사 할 때 앞에 공백을 같이 복사 해버려서 오류 발생, 오류내용이 안떴는데 껐다가 키니까 다시 오류 내용 나옴;;
-
customWidget좀 씁시다.. 스타일 만들 때 쌩 노가다;;
-
회원가입 왜 안되는지 모르겠음.. null-safety도 주고 했는데.. 실패.. 뭘까요??..
main.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:secret/controller/auth_controller.dart';
import 'package:secret/controller/secret_controller.dart';
import 'package:secret/util/pages.dart';
import 'controller/login_controller.dart';
import 'controller/signup_controller.dart';
import 'controller/upload_controller.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return GetMaterialApp(
getPages: AppPages.pages, //페이지 관리 위해 모아놓은 리스트 호출
title: "SecretCat",
initialBinding: BindingsBuilder(() {
//시스템이 시작될 때 마다 자동으로 메모리에 올리기
Get.put(AuthController());
Get.lazyPut(() => LoginController()); //login컨트롤러 대기상태로 올림
Get.put(SecretController(), permanent: true);
// Get.lazyPut(() => SecretController());
Get.lazyPut(() => SignupController());
Get.put(UploadController(),
permanent: true); //uploading 한번 하고 나가면 컨트롤러 메모리에서 지워짐
//계속 남아있게 하려고 이렇게 설정, 어떤 상황이어도 컨트롤러 종료 안해
}),
initialRoute: 'login', //첫화면 로그인 페이지로
);
}
}
controller/auth_controller.dart
import 'package:get/get.dart';
import 'package:secret/model/profile.dart';
import 'package:secret/util/api_routes.dart';
import 'package:secret/view/page/main_page.dart';
import 'package:dio/dio.dart';
class AuthController extends GetxController {
//profile을 담을거임, 변하고 null일 수도 Rxn
final Rxn<Profile> _profile = Rxn<Profile>(); //null값으로 일단 하나 만들기
Dio dio = Dio();
Profile? get profile =>
_profile.value; //profile private이니까 get으로 꺼낼 수 있도록 새로 하나 더 만든다 생각해
//메소드
login(String id, String pw) async {
//호출 시 아이디와 pw 가지고 호출 해줘야돼, url routes에 정의 해놨음, post방식으로 하기로 했음
var res =
await dio.post(ApiRoutes.login, data: {'identity': id, 'password': pw});
if (res.statusCode == 200) {
//핵심 데이터 뽑기, 가공
var data = Map<String, dynamic>.from(
res.data['record']); //record에 우리가 원하는 값이 있음, 정보 형태 Map으로 바꾸기
_profile(Profile.fromMap(data)); //_profile에 현재 정보 등록
print(res.data);
}
}
signup(String email, String pw, String pw2, String username) async {
//정보보내기, url요청
var res = await dio.post(ApiRoutes.secrets, data: {
//보내는 데이터 정보
'email': email,
'password': pw,
'passwordConfirm': pw2,
'username': username,
});
//데이터 받으면 profile등록
_profile(Profile.fromMap(res.data));
}
logout() {
_profile.value = null; //로그아웃 누르면 값을 null로 바꿈, 로그인페이지로 이동
}
_handleOnProfileChanged(value) {
if (value != null) {
//로그인이 된거니까 main으로 이동
Get.toNamed(MainPage.route);
return; //없으면 메인 갔다가 밑에 있는 로그인 화면으로 이동
}
//로그인화면으로 이동
Get.toNamed('/login');
return;
}
//프로필이 바뀔 때 마다 새로운 페이지로 이동
@override
void onInit() {
// TODO: implement onInit
super.onInit();
//위에 함수 호출 해서 값 가지고 감
ever(_profile, _handleOnProfileChanged);
}
}
controller/login_controller.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:secret/controller/auth_controller.dart';
class LoginController extends GetxController {
var idController = TextEditingController();
var pwController = TextEditingController();
login() {
//authcontroller에 정의되어 있는 로그인함수 호출, 아이디,pw넘기기로 했으니까
//컨트롤러에 .text로 같이 넘겨줘
Get.find<AuthController>().login(idController.text, pwController.text);
}
}
controller/secret_controller.dart
import 'package:dio/dio.dart';
import 'package:get/get.dart';
import 'package:secret/util/api_routes.dart';
import '../model/secret.dart';
class SecretController extends GetxController {
//여러개 비밀을 가지고 있으니 List로
final RxList<Secret> _secrets = <Secret>[].obs;
Dio dio = Dio();
List<Secret> get secrets => _secrets; //private 보낼 수 있도록
//내용 가져오는 함수
fetchSecrets() async {
//dio통해 데이터 가져오고 secret에 가져오기
var res = await dio.get(ApiRoutes.secrets);
print(res);
//items에 있는 내용 Map형태로 바꾸기
var items = List<Map<String, dynamic>>.from(res.data['items']);
_secrets(items.map((e) => Secret.fromMap(e)).toList()); //한개한개 다 매칭 해서 넣기
//_secrets에 정보 넣어서 보내기
}
@override
void onInit() {
// TODO: implement onInit
super.onInit();
fetchSecrets(); //컨트롤러 생성되면 바로 실행
}
}
controller/signup_controller.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:secret/controller/auth_controller.dart';
import 'package:secret/model/profile.dart';
class SignupController extends GetxController {
var emailController = TextEditingController(); //이메일
var usernameController = TextEditingController(); //이름
var pwController = TextEditingController(); //비밀번호
var pw2Controller = TextEditingController(); //비밀번호 확인
signup() async {
//함수 정의
//Auth에 있는 정보가져와
Get.find<AuthController>().signup(emailController.text, pwController.text,
pw2Controller.text, usernameController.text); //정보 보내기
}
}
controller/upload_controller.dart
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:secret/util/api_routes.dart';
class UploadController extends GetxController {
var inputController = TextEditingController(); //비밀 작성하는 컨트롤러
Dio dio = Dio();
upload() async {
if (inputController.text.isEmpty) return; //비어있으면 아무것도 안되게
var res = await dio.post(ApiRoutes.upload, data: {
'secret': inputController.text //data같이 보내기 컨트롤러에 있는 내가 작성한 비밀
});
inputController.text = ''; //비밀올리면 작성했던 내용 지움
print(res); //내가 작성한 비밀내용 보임
}
}
model/profile.dart
import 'dart:convert';
// ignore_for_file: public_member_api_docs, sort_constructors_first
class Profile {
String? email;
String username;
String name;
Profile({
required this.email,
required this.username,
required this.name,
});
Map<String, dynamic> toMap() {
return <String, dynamic>{
'email': email,
'username': username,
'name': name,
};
}
factory Profile.fromMap(Map<String, dynamic> map) {
return Profile(
email: map['email'],
username: map['username'] as String,
name: map['name'] as String,
);
}
String toJson() => json.encode(toMap());
factory Profile.fromJson(String source) =>
Profile.fromMap(json.decode(source) as Map<String, dynamic>);
}
model/secret.dart
import 'dart:convert';
// ignore_for_file: public_member_api_docs, sort_constructors_first
class Secret {
String secret; //비밀 내용
String author; //비밀 작성자
Secret({
required this.secret,
required this.author,
});
Map<String, dynamic> toMap() {
return <String, dynamic>{
'secret': secret,
'author': author,
};
}
factory Secret.fromMap(Map<String, dynamic> map) {
return Secret(
secret: map['secret'] as String,
author: map['author'] as String,
);
}
String toJson() => json.encode(toMap());
factory Secret.fromJson(String source) =>
Secret.fromMap(json.decode(source) as Map<String, dynamic>);
}
util/api_routes.dart
class ApiRoutes {
static String login =
"http://52.79.115.43:8090/api/collections/users/auth-with-password";
static String signup =
"http://52.79.115.43:8090/api/collections/users/records";
static String secrets =
"http://52.79.115.43:8090/api/collections/secrets/records?sort=-created";
static String users =
"http://52.79.115.43:8090/api/collections/users/records?sort=-created";
static String upload =
"http://52.79.115.43:8090/api/collections/secrets/records";
}
util/pages.dart
import 'package:get/get.dart';
import 'package:secret/view/page/login_page.dart';
import 'package:secret/view/page/main_page.dart';
import 'package:secret/view/page/secret_page.dart';
import 'package:secret/view/page/setting_page.dart';
import 'package:secret/view/page/signup_page.dart';
import 'package:secret/view/page/upload_page.dart';
class AppPages {
static final pages = [
//라우팅 연동위해서
GetPage(name: MainPage.route, page: () => const MainPage()),
GetPage(name: '/login', page: () => const LoginPage()),
GetPage(name: SecretPage.route, page: () => const SecretPage()),
GetPage(name: SettinPage.route, page: () => const SettinPage()),
GetPage(name: SignupPage.route, page: () => const SignupPage()),
GetPage(name: UploadPage.route, page: () => const UploadPage()),
];
}
view/page/login_page.dart
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:secret/controller/login_controller.dart';
import 'package:secret/view/page/signup_page.dart';
class LoginPage extends GetView<LoginController> {
const LoginPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromARGB(255, 17, 100, 93),
body: Center(
child: Padding(
padding: const EdgeInsets.all(16),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"비밀듣는 고양이",
style: TextStyle(
color: Colors.white,
fontSize: 15,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 10,
),
CircleAvatar(
radius: 56,
backgroundColor: Colors.white38,
backgroundImage: AssetImage('assets/images/cat.jpg'),
),
SizedBox(
height: 20,
),
//id, 패스워드 입력하는 Textfield만들기
TextField(
decoration: InputDecoration(
label: Text('ID'),
hintText: "ID를 입력하세요",
labelStyle: TextStyle(color: Colors.redAccent),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide(width: 1, color: Colors.redAccent),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide(width: 1, color: Colors.redAccent),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
),
controller: controller
.idController, //Getview에서 온 LoginController안에 아이디컨트롤러가져오기
),
SizedBox(
height: 15,
),
TextField(
decoration: InputDecoration(
label: Text('Password'),
hintText: "비밀번호를 입력하세요",
labelStyle: TextStyle(color: Colors.redAccent),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide(width: 1, color: Colors.redAccent),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide(width: 1, color: Colors.redAccent),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
),
controller: controller.pwController,
),
ElevatedButton(
onPressed: controller.login, //로그인컨트롤러의 로그인 함수 실행 ()안써야돼
child: const Text("Login")),
TextButton(
onPressed: () => Get.toNamed(SignupPage.route),
child: Text("회원가입하기"))
],
),
),
),
),
);
}
}
view/page/main_page.dart
import 'package:flutter/src/widgets/container.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:secret/view/page/secret_page.dart';
import 'package:secret/view/page/setting_page.dart';
import 'package:secret/view/page/upload_page.dart';
class MainPage extends StatelessWidget {
const MainPage({super.key});
static const route = '/main';
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromARGB(255, 17, 100, 93),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
"비밀듣는 고양이",
style: TextStyle(
color: Colors.white, fontSize: 15, fontWeight: FontWeight.bold),
),
SizedBox(
height: 20,
),
CircleAvatar(
radius: 56,
backgroundColor: Colors.white38,
backgroundImage: AssetImage('assets/images/cat.jpg'),
),
SizedBox(
height: 15,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: ListTile(
tileColor: Colors.orangeAccent,
title: const Text(
"비밀보기",
style: TextStyle(
color: Colors.white,
fontSize: 15,
fontWeight: FontWeight.bold),
),
trailing: CircleAvatar(
radius: 15,
backgroundColor: Colors.white38,
backgroundImage: AssetImage('assets/images/cat.jpg'),
),
onTap: () => Get.toNamed(SecretPage.route), //페이지이동
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: ListTile(
tileColor: Colors.orangeAccent,
trailing: CircleAvatar(
radius: 15,
backgroundColor: Colors.white38,
backgroundImage: AssetImage('assets/images/cat.jpg'),
),
title: const Text(
"비밀올리기",
style: TextStyle(
color: Colors.white,
fontSize: 15,
fontWeight: FontWeight.bold),
),
onTap: () => Get.toNamed(UploadPage.route),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: ListTile(
tileColor: Colors.orangeAccent,
trailing: CircleAvatar(
radius: 15,
backgroundColor: Colors.white38,
backgroundImage: AssetImage('assets/images/cat.jpg'),
),
title: const Text(
"앱설정",
style: TextStyle(
color: Colors.white,
fontSize: 15,
fontWeight: FontWeight.bold),
),
onTap: () => Get.toNamed(SettinPage.route),
),
)
],
),
);
}
}
view/page/secret_page.dart
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:secret/controller/secret_controller.dart';
var backgroundImg =
"https://images.unsplash.com/photo-1574144611937-0df059b5ef3e?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MjR8fGNhdHxlbnwwfHwwfHw%3D&auto=format&fit=crop&w=500&q=60";
class SecretPage extends GetView<SecretController> {
const SecretPage({super.key});
static const route = '/secret';
@override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true, //body가 앱바 영역까지 차지
appBar: AppBar(
centerTitle: false,
title: Text("뒤로가기"),
backgroundColor: Colors.transparent,
elevation: 0,
),
body: Container(
alignment: Alignment.center,
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(backgroundImg),
fit: BoxFit.cover,
colorFilter:
ColorFilter.mode(Colors.black54, BlendMode.darken))),
child: Obx(() => PageView.builder(
itemCount: controller.secrets.length, //get을 통해 가져온 secret 갯수만큼
itemBuilder: (context, index) => Center(
child: Text(
controller.secrets[index].secret,
style:
TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
)),
)),
),
);
}
}
view/page/setting_page.dart
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:secret/controller/auth_controller.dart';
//원하는 함수 변수들 다 Auth에 해놨어 그거 가져와
class SettinPage extends GetView<AuthController> {
const SettinPage({super.key});
static const route = '/setting';
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromARGB(255, 17, 100, 93),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
"내 정보!",
style: TextStyle(
color: Colors.white, fontSize: 15, fontWeight: FontWeight.bold),
),
SizedBox(
height: 20,
),
CircleAvatar(
radius: 56,
backgroundColor: Colors.white38,
backgroundImage: AssetImage('assets/images/cat.jpg'),
),
SizedBox(
height: 15,
),
ListTile(
title: Text(controller.profile!
.username), //?로 get을 설정해 놨음. 이미 로그인 한 상태이기 때문에 무조건 데이터 있어 !
subtitle: Text(controller.profile!.name),
),
ElevatedButton(
onPressed: controller.logout, child: const Text("로그아웃")),
//Auth에 있는 로그아웃 함수 실행, 이미 정의해놔서 ()쓰지마
],
),
);
}
}
view/page/signup_page.dart
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 '../../controller/signup_controller.dart';
class SignupPage extends GetView<SignupController> {
const SignupPage({super.key});
static const route = '/signup';
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromARGB(255, 17, 100, 93),
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
"비밀듣는 고양이",
style: TextStyle(
color: Colors.white,
fontSize: 15,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 20,
),
CircleAvatar(
radius: 56,
backgroundColor: Colors.white38,
backgroundImage: AssetImage('assets/images/cat.jpg'),
),
SizedBox(
height: 15,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
decoration: InputDecoration(
label: Text('Email'),
hintText: "Email을 입력하세요",
labelStyle: TextStyle(color: Colors.redAccent),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide(width: 1, color: Colors.redAccent),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide(width: 1, color: Colors.redAccent),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
),
controller: controller.emailController, //컨트롤러 연결
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
decoration: InputDecoration(
label: Text('Username'),
hintText: "이름을 입력하세요",
labelStyle: TextStyle(color: Colors.redAccent),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide(width: 1, color: Colors.redAccent),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide(width: 1, color: Colors.redAccent),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
),
controller: controller.usernameController, //컨트롤러 연결
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
decoration: InputDecoration(
label: Text('Password'),
hintText: "비밀번호를 입력하세요",
labelStyle: TextStyle(color: Colors.redAccent),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide(width: 1, color: Colors.redAccent),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide(width: 1, color: Colors.redAccent),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
),
controller: controller.pwController,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
decoration: InputDecoration(
label: Text('PwConfirm'),
hintText: "비밀번호를 다시 입력하세요",
labelStyle: TextStyle(color: Colors.redAccent),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide(width: 1, color: Colors.redAccent),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide(width: 1, color: Colors.redAccent),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
),
controller: controller.pw2Controller,
),
),
ElevatedButton(onPressed: controller.signup, child: Text("회원가입"))
],
),
),
);
}
}
view/page/upload_page.dart
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:secret/controller/upload_controller.dart';
class UploadPage extends GetView<UploadController> {
const UploadPage({super.key});
static const route = '/upload';
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color.fromARGB(255, 17, 100, 93),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"UploadPage",
style: TextStyle(
color: Colors.white,
fontSize: 15,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 15,
),
CircleAvatar(
radius: 56,
backgroundColor: Colors.white38,
backgroundImage: AssetImage('assets/images/cat.jpg'),
),
SizedBox(
height: 15,
),
TextField(
controller: controller.inputController,
maxLines: 10,
minLines: 8,
decoration: InputDecoration(
filled: true,
fillColor: Colors.white24,
hintText: "비밀을 입력하세요!!!!"),
),
ElevatedButton(onPressed: controller.upload, child: Text("Upload"))
],
));
}
}