import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class MainScreen extends StatefulWidget {
const MainScreen({super.key});
@override
State<MainScreen> createState()=> _MainScreen();
}
class _MainScreen extends State<MainScreen>{
//자기소개 값을 입력받는 컨트롤러
TextEditingController introduceController = TextEditingController();
//자기소개 수정 모드
bool isEditMode=false;
//초기 상태
@override
void initState() {
super.initState();
getIntroduceData();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: Icon(
Icons.accessibility_new,
color: Colors.black,
size: 30,
),
backgroundColor: Colors.white,
elevation: 0,
title: Text(
'발전하는 개발자 정은하입니다.',
style: TextStyle(
fontSize: 14,
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.all(16),
width: double.infinity,
height: 140,
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.asset(
'assets/user.png',
//fit: BoxFit.cover,
),
),
),
// 이름 섹션
Container(
margin: EdgeInsets.symmetric(vertical: 10,horizontal: 16),
child: Row(
children: const [
SizedBox(
width: 200,
child: Text(
'이름',
style: TextStyle(fontWeight: FontWeight.bold))),
Text('정은하'),
],
),
),
// 나이 섹션
Container(
margin: EdgeInsets.symmetric(vertical: 10,horizontal: 16),
child: Row(
children: const [
SizedBox(
width: 200,
child: Text(
'나이',
style: TextStyle(fontWeight: FontWeight.bold))),
Text('25'),
],
),
),
//취미 섹션
Container(
margin: EdgeInsets.symmetric(vertical: 10,horizontal: 16),
child: Row(
children: const [
SizedBox(
width: 200,
child: Text(
'취미',
style: TextStyle(fontWeight: FontWeight.bold))),
Text('영화 보기'),
],
),
),
//직업 섹션
Container(
margin: EdgeInsets.symmetric(vertical: 10,horizontal: 16),
child: Row(
children: const [
SizedBox(
width: 200,
child: Text(
'직업',
style: TextStyle(fontWeight: FontWeight.bold))),
Text('대학생'),
],
),
),
//학력 섹션
Container(
margin: EdgeInsets.symmetric(vertical: 10,horizontal: 16),
child: Row(
children: const [
SizedBox(
width: 200,
child: Text(
'학력',
style: TextStyle(fontWeight: FontWeight.bold))),
Text('경기대학교 컴퓨터공학과'),
],
),
),
//MBTI 섹션
Container(
margin: EdgeInsets.symmetric(vertical: 10,horizontal: 16),
child: Row(
children: const [
SizedBox(
width: 200,
child: Text(
'MBTI',
style: TextStyle(fontWeight: FontWeight.bold))),
Text('ESTJ'),
],
),
),
//자기소개서 섹션
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
margin: EdgeInsets.only(left: 16,top: 16),
child: Text(
'자기소개서',
style: TextStyle(fontSize: 18,fontWeight: FontWeight.bold)),
),
GestureDetector(child:
Container(
margin: EdgeInsets.only(right: 16, top: 16),
child: Icon(
Icons.mode_edit,
color: isEditMode == true ? Colors.blueAccent : Colors.black,
size: 24,
),
),onTap: () async {
FocusScope.of(context).unfocus();
if(isEditMode == false){
setState(() {
//update widget
isEditMode=true;
});
}
else{
if(introduceController.text.isEmpty){
//snackbar 메세지로 사용자에게 안내하기
var snackBar=SnackBar(content: Text('자기소개 입력 값이 비어있습니다.'),
duration: Duration(seconds: 2),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
//저장 기능을 수행하지 않고 종료
return;
}
//자기 소개서 저장 로직 구현
var sharedPref=await SharedPreferences.getInstance();
sharedPref.setString('introduce', introduceController.text);
setState(() {
isEditMode=false;
});
}
},)
],
),
Container(
margin: EdgeInsets.symmetric(horizontal: 16,vertical: 8),
child: TextField(
maxLines: 5,
controller: introduceController ,
enabled: isEditMode,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Color(0xffd9d9d9))
)
),),
),
],
),
),
);
}
Future<void> getIntroduceData() async {
//기존에 저장되어 있는 자기소개서 데이터가 있으면 로드해오기
var sharedPref= await SharedPreferences.getInstance();
String introduceMsg=sharedPref.getString('introduce').toString();
introduceController.text = introduceMsg ?? "";
}
}
class MainScreen extends StatefulWidget {
const MainScreen({super.key});
@override
State<MainScreen> createState()=> _MainScreen();
}
MainScreen: 화면 상태 관리
_MainScreen: 해당 화면의 상태를 다룸
State: State가 MainScreen 위젯이 화면에 표시될 때 해당 위젯의 상태를 관리
createState: MainScreen 위젯의 상태를 생성
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.all(16),
width: double.infinity,
height: 140,
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.asset(
'assets/user.png',
//fit: BoxFit.cover,
),
),
),
// 이름 섹션
Container(
margin: EdgeInsets.symmetric(vertical: 10,horizontal: 16),
child: Row(
children: const [
SizedBox(
width: 200,
child: Text(
'이름',
style: TextStyle(fontWeight: FontWeight.bold))),
Text('정은하'),
],
),
),
// 나이 섹션
Container(
margin: EdgeInsets.symmetric(vertical: 10,horizontal: 16),
child: Row(
children: const [
SizedBox(
width: 200,
child: Text(
'나이',
style: TextStyle(fontWeight: FontWeight.bold))),
Text('25'),
],
),
),
//취미 섹션
Container(
margin: EdgeInsets.symmetric(vertical: 10,horizontal: 16),
child: Row(
children: const [
SizedBox(
width: 200,
child: Text(
'취미',
style: TextStyle(fontWeight: FontWeight.bold))),
Text('영화 보기'),
],
),
),
//직업 섹션
Container(
margin: EdgeInsets.symmetric(vertical: 10,horizontal: 16),
child: Row(
children: const [
SizedBox(
width: 200,
child: Text(
'직업',
style: TextStyle(fontWeight: FontWeight.bold))),
Text('대학생'),
],
),
),
//학력 섹션
Container(
margin: EdgeInsets.symmetric(vertical: 10,horizontal: 16),
child: Row(
children: const [
SizedBox(
width: 200,
child: Text(
'학력',
style: TextStyle(fontWeight: FontWeight.bold))),
Text('경기대학교 컴퓨터공학과'),
],
),
),
//MBTI 섹션
Container(
margin: EdgeInsets.symmetric(vertical: 10,horizontal: 16),
child: Row(
children: const [
SizedBox(
width: 200,
child: Text(
'MBTI',
style: TextStyle(fontWeight: FontWeight.bold))),
Text('ESTJ'),
],
),
),
//자기소개서 섹션
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
margin: EdgeInsets.only(left: 16,top: 16),
child: Text(
'자기소개서',
style: TextStyle(fontSize: 18,fontWeight: FontWeight.bold)),
),
GestureDetector(child:
Container(
margin: EdgeInsets.only(right: 16, top: 16),
child: Icon(
Icons.mode_edit,
color: isEditMode == true ? Colors.blueAccent : Colors.black,
size: 24,
),
),onTap: () async {
FocusScope.of(context).unfocus();
if(isEditMode == false){
setState(() {
//update widget
isEditMode=true;
});
}
else{
if(introduceController.text.isEmpty){
//snackbar 메세지로 사용자에게 안내하기
var snackBar=SnackBar(content: Text('자기소개 입력 값이 비어있습니다.'),
duration: Duration(seconds: 2),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
//저장 기능을 수행하지 않고 종료
return;
}
//자기 소개서 저장 로직 구현
var sharedPref=await SharedPreferences.getInstance();
sharedPref.setString('introduce', introduceController.text);
setState(() {
isEditMode=false;
});
}
},)
],
),
Container(
margin: EdgeInsets.symmetric(horizontal: 16,vertical: 8),
child: TextField(
maxLines: 5,
controller: introduceController ,
enabled: isEditMode,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Color(0xffd9d9d9))
)
),),
),
],
),
),
);
}
SingleChildScrollView: 자식 위젯이 화면에 모두 나타나지 않는 경우 스크롤을 제공하여 추가 콘텐츠에 접근할 수 있도록 함crossAxisAlignment: CrossAxisAlignment.start : 자식 위젯을 교차 축의 시작 부분에 정렬
EdgeInsets.all(16): 모든 네 방향(상, 하, 좌, 우)으로 16 픽셀의 마진을 의미
width: double.infinity: 최대 가능한 가로 길이
ClipRRect:주어진 사각형 영역을 둥글게 만들어 특정 모서리를 둥글게 잘라내는 역할
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
margin: EdgeInsets.only(left: 16,top: 16),
child: Text(
'자기소개서',
style: TextStyle(fontSize: 18,fontWeight: FontWeight.bold)),
),
GestureDetector(child:
Container(
margin: EdgeInsets.only(right: 16, top: 16),
child: Icon(
Icons.mode_edit,
color: isEditMode == true ? Colors.blueAccent : Colors.black,
size: 24,
),
),onTap: () async {
FocusScope.of(context).unfocus();
if(isEditMode == false){
setState(() {
//update widget
isEditMode=true;
});
}
else{
if(introduceController.text.isEmpty){
//snackbar 메세지로 사용자에게 안내하기
var snackBar=SnackBar(content: Text('자기소개 입력 값이 비어있습니다.'),
duration: Duration(seconds: 2),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
//저장 기능을 수행하지 않고 종료
return;
}
//자기 소개서 저장 로직 구현
var sharedPref=await SharedPreferences.getInstance();
sharedPref.setString('introduce', introduceController.text);
setState(() {
isEditMode=false;
});
}
},)
],
),
Container(
margin: EdgeInsets.symmetric(horizontal: 16,vertical: 8),
child: TextField(
maxLines: 5,
controller: introduceController ,
enabled: isEditMode,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: BorderSide(color: Color(0xffd9d9d9))
)
),),
),
],
),
-mainAxisAlignment: MainAxisAlignment.spaceBetween : 자식 위젯 사이의 여백을 균등하게 배치하고, 부모 위젯의 시작과 끝 부분에 각각 정렬
-GestureDetector: 사용자의 터치 제스처에 반응하는 데 사용
-onTap: () async : 터치 이벤트를 감지했을 때 실행되는 콜백 함수를 정의. async 키워드가 있어서 비동기 함수로서 await 키워드를 사용할 수 있음
-FocusScope.of(context).unfocus(): 보드 등의 입력 포커스를 제거
-setState 함수: StatefulWidget 클래스에서 상태를 변경할 때 사용되는 메서드
-ScaffoldMessenger.of(context): ScaffoldMessenger
var sharedPref=await SharedPreferences.getInstance();
sharedPref.setString('introduce', introduceController.text);
setState(() {
isEditMode=false;
});
-var sharedPref = await SharedPreferences.getInstance();: SharedPreferences.getInstance()-> 앱의 로컬 저장소에 액세스하기 위한 SharedPreferences 인스턴스를 얻는 메서드
-sharedPref.setString('introduce', introduceController.text);: setString 메서드를 사용하여 'introduce'라는 키로 사용자의 소개 정보를 저장, introduceController.text는 사용자가 입력한 텍스트 값을 나타냄
Future<void> getIntroduceData() async {
//기존에 저장되어 있는 자기소개서 데이터가 있으면 로드해오기
var sharedPref= await SharedPreferences.getInstance();
String introduceMsg=sharedPref.getString('introduce').toString();
introduceController.text = introduceMsg ?? "";
}
-introduceController.text = introduceMsg ?? ""; : introduceController: introduceMsg가 null이면 빈 문자열("")을 설정