flutter-나만의 명함 만들기(2)

정은하·2024년 2월 11일

Flutter

목록 보기
7/8
post-thumbnail

main_screen.dart

전체 코드

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

  • of(context) 메서드: 현재 컨텍스트에 대한 ScaffoldMessenger 인스턴스를 가져옴.
  • .showSnackBar(snackBar): showSnackBar 메서드는 스낵바를 화면에 표시하는 역할

자기소개서 저장 로직

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는 사용자가 입력한 텍스트 값을 나타냄

  • isEditMode=false: 사용자가 소개 정보를 입력하고 저장한 후에 편집 모드를 비활성화하여 더 이상 수정할 수 없게 만드는 역할

저장되어 있는 데이터 불러오기 로직

Future<void> getIntroduceData() async {
  //기존에 저장되어 있는 자기소개서 데이터가 있으면 로드해오기
  var sharedPref= await SharedPreferences.getInstance();
  String introduceMsg=sharedPref.getString('introduce').toString();
  introduceController.text = introduceMsg ?? "";
}

-introduceController.text = introduceMsg ?? ""; : introduceController: introduceMsg가 null이면 빈 문자열("")을 설정

profile
If you remain stagnant, you won't achieve anything

0개의 댓글