스마트시대

AUTHENTICATION4.3 Sign Up Form 4.4 Username Screen 4.5 FormButton 본문

Programing/Flutter

AUTHENTICATION4.3 Sign Up Form 4.4 Username Screen 4.5 FormButton

스마트시대 2023. 5. 5. 00:28
728x90

4.3 Sign Up Form

 

email , username 을 입력받을 widget을 2개 만든다.

signup 화면에서 gesturedector 로 email AuthButton을 감싼다.

Method에 ‘_’ 접두어를 붙여서 private 와 같이 만들수 있다.

appBar, Scaffold Theme 를 main.dart에 만들기 Theme는 모든곳에서 적용됨. 오버라이드 가능 Sizes 는 ‘+’ 연산 가능

 

sign_up_screen

        builder: (context) => const LoginScreen(),
      ),
    );
  }


----------------
  // 버튼이나 글씨 누르면 다음 화면으로 넘어가는 함수
  void _onEmailTap(BuildContext context) {
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (context) => const EmailScreen(),
      ),
    );
  }
  
  ----------------

  @override
  Widget build(BuildContext context) {
  
  
  
                  textAlign: TextAlign.center,
              ),
              Gaps.v40,
              
          ----------------  ----------------
              // email인증 하기 위해 클릭 가능하게
              GestureDetector(
                onTap: () => _onEmailTap(context),
                
             ----------------  ----------------
                child: const AuthButton(
                  icon: FaIcon(FontAwesomeIcons.user),
                  text: 'Use email and password',
                ),
              ),
              
            
              Gaps.v16,
              const AuthButton(

 

email_screen->이거 username_screen에 할 내용 4-4에서 수정

import 'package:flutter/material.dart';
import 'package:tiktok_clone/constants/gaps.dart';
import 'package:tiktok_clone/constants/sizes.dart';

class EmailScreen extends StatelessWidget {
  const EmailScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        title: const Text(
          "Sign up",
        ),
      ),
      // 좀더 텍스트 간격 정렬을 위해 padding
      body: Padding(
        padding: const EdgeInsets.symmetric(
          horizontal: Sizes.size36,
        ),
        child: Column(
          // 텍스트 정렬하기
          crossAxisAlignment: CrossAxisAlignment.start,
          children: const [
            Gaps.v40,
            Text(
              "Create username",
              style: TextStyle(
                fontSize: Sizes.size24,
                fontWeight: FontWeight.w700,
              ),
            ),
            Gaps.v8,
            Text(
              "You can always change this later.",
              style: TextStyle(
                fontSize: Sizes.size16,
                color: Colors.black54,
                fontWeight: FontWeight.w400,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

 

main.dart

  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'TikTok Clone',
      theme: ThemeData(
      
      
      
      --------------
        // 자세히 보면 scaffold는 약간 회색빛을 띄는데 이게 짜증나니 여기서 강제 흰색으로 지정
        scaffoldBackgroundColor: Colors.white,
        --------------
        
        primaryColor: const Color(0xFFE9435A),
    // appBar에서 글씨 지정하는 것도 어느정도 고정이니 여기서 전역적으로 지정하자
        appBarTheme: const AppBarTheme(
          foregroundColor: Colors.black,
          backgroundColor: Colors.white,
          // appbar와 navigationbar의 경계에 있는 그림자 수치
          elevation: 0,
          titleTextStyle: TextStyle(
            color: Colors.black,
            fontSize: Sizes.size16 + Sizes.size2,
            fontWeight: FontWeight.w600,
          ),
        ),
      ),
      
              --------------        --------------
      home: const SignUpScreen(),
    );
  }
}

 

 

 4.4 Username Screen

 

controller란? addlistener 가 controller에 어떤 식의 구조인지?

Controller는 user interaction을 받아들이고 state를 관리하는데 용이한 클래스. javascript에서 listener를 붙여서 user interaction에 해당하는 액션을 만들어주는거처럼 addlistener로 controller가 유저의 행동을 listen하고 그에따라 액션을 취함

 

TextField

InputDecoration : UnderlineInputBorder cursorColor , enableBorder, focusedBorder, 텍스트 입력을 위해서는 controller가 필요하므로 StateWidget으로 바꾸어주어야 함.

controller를 만들고, TextField 에 설정하고, TextField가 변할때 마다 setState를 하여 버튼이 틱톡 프라이머리 칼라 또는 회색이 되도록 해준다.

버튼 칼라가 부드럽게 바뀌게 하기 위해서는 AnimatedContainer를 사용함. animation은 duration 값으로 조정. 

 

 

 

 

// Controller는 코드, 메서드 등으로 이 textField아 같은 위젯을 컨트롤할 수 있게 해준다.

Controller를 사용할 수 있게 해주기 위해서는 statelesswidget을 statefulwidget으로 바꿔줘야 한다.

 

 

 

user_screen.dart

import 'package:flutter/material.dart';
import 'package:tiktok_clone/constants/gaps.dart';
import 'package:tiktok_clone/constants/sizes.dart';

class UsernameScreen extends StatefulWidget {
  const UsernameScreen({super.key});

  @override
  State<UsernameScreen> createState() => _UsernameScreenState();
}

class _UsernameScreenState extends State<UsernameScreen> {
// Controller는 코드, 메서드 등으로 이 textField아 같은 위젯을 컨트롤할 수 있게 해준다.
  final TextEditingController _usernameController = TextEditingController();

// _username 정의하기
  String _username = "";

// TextField의 텍스트 변화를 감지하기 위한 이벤트 리스너
  @override
  void initState() {
    _usernameController.addListener(
      () {
        // print(_usernameController.text);
        setState(() {
          // 여기서 state를 저장해준다.
          _username = _usernameController.text;
        });
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        title: const Text(
          "Sign up",
        ),
      ),
      // 좀더 텍스트 간격 정렬을 위해 padding
      body: Padding(
        padding: const EdgeInsets.symmetric(
          horizontal: Sizes.size36,
        ),
        child: Column(
          // 텍스트 정렬하기
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Gaps.v40,
            const Text(
              "Create username",
              style: TextStyle(
                fontSize: Sizes.size24,
                fontWeight: FontWeight.w700,
              ),
            ),
            Gaps.v8,
            const Text(
              "You can always change this later.",
              style: TextStyle(
                fontSize: Sizes.size16,
                color: Colors.black54,
              ),
            ),
            Gaps.v16,
            // 유저가 문자를 입력할 수 있는 위젯
            TextField(
              // TextEditingController 함수 만들었으니 여기다 정의
              controller: _usernameController,
              decoration: InputDecoration(
                hintText: "Username",
                // 글자 밑 부분 border 회색으로 바꿔주려면 이 2개 필요
                enabledBorder: UnderlineInputBorder(
                  borderSide: BorderSide(
                    color: Colors.grey.shade400,
                  ),
                ),
                focusedBorder: UnderlineInputBorder(
                  borderSide: BorderSide(
                    color: Colors.grey.shade400,
                  ),
                ),
              ),
              // 핑크 커서 만들기
              cursorColor: Theme.of(context).primaryColor,
            ),
            Gaps.v16,

            // Next button 만들기
            FractionallySizedBox(
              widthFactor: 1,

              // 색변화할 때 효과를 주기 위해 AnimatedContainer로 변경
              child: AnimatedContainer(
                padding: const EdgeInsets.symmetric(
                  vertical: Sizes.size16,
                ),
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(Sizes.size5),
                  color: _username.isEmpty
                      ? Colors.grey.shade300 // if
                      : Theme.of(context).primaryColor, // else
                ),

                // AnimatedContainer에는 이게 필요
                duration: const Duration(microseconds: 500),

                child: const Text(
                  "Next",
                  textAlign: TextAlign.center,
                  style: TextStyle(
                    color: Colors.white,
                    fontWeight: FontWeight.w600,
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

 

4.5 FormButton

 

TextStyle 애미메이션을 위해 AnimatedDefaultTextStyle 사용

하지만 child 까지 변화시켜주지는 않는다.

여기서 보면 글씨는 바로 회색에서 흰색으로 바뀌는걸 알 수 있다.

그 때 child에 필요한 위젯은 AnimatedDefaultTextStyle 이다.

--------------------
                // child에 별도로 이 위젯이 필요
                child: AnimatedDefaultTextStyle(
                  duration: const Duration(microseconds: 500),
                  // Duration과 함께 style도 여기로 옮겨야 함
                  style: TextStyle(
                    color: _username.isEmpty ? Colors.grey : Colors.white,
                    fontWeight: FontWeight.w600,
                  ),
                  
                  --------------------
                  child: const Text(
                    "Next",

 

재사용을 위해 FormButton을 widget으로 만듬 →

-------------------

전구에서 ExtractWidget 하고 에러나는 부분 import 넣어주고.

Widget으로 만들때는 생성자 매개변수을 어떻게 넣어주어야 하는지 고민필요.

 

패러메터 정의를 좀 더 수월하게 하기 위해 아래와 같이 코드 변경

            ),
            Gaps.v16,
            
                        -------------------

            // Next button 만들기
            FormButton(
              disabled: _username.isEmpty,
            ),
          ],
        ),
      ),
    );
  }
}

class FormButton extends StatelessWidget {
  const FormButton({
    super.key,
    required this.disabled,
  });

  final bool disabled;

            -------------------

  @override
  Widget build(BuildContext context) {
    return FractionallySizedBox(
      widthFactor: 1,

      // 색변화할 때 효과를 주기 위해 AnimatedContainer로 변경
      child: AnimatedContainer(
        padding: const EdgeInsets.symmetric(
          vertical: Sizes.size16,
        ),
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(Sizes.size5),
          
                      -------------------
          color: disabled
              ? Colors.grey.shade300 // if
              : Theme.of(context).primaryColor, // else
        ),
                    -------------------

        // AnimatedContainer에는 이게 필요
        duration: const Duration(microseconds: 500),

        // child에 별도로 이 위젯이 필요
        child: AnimatedDefaultTextStyle(
          duration: const Duration(microseconds: 500),
          // Duration과 함께 style도 여기로 옮겨야 함
          style: TextStyle(
                      -------------------
            color: disabled ? Colors.grey : Colors.white,
            fontWeight: FontWeight.w600,
            
            -------------------
          ),
          child: const Text(
            "Next",
            textAlign: TextAlign.center,
          ),
        ),

 

그리고 form_button.dart파일을 새로 만들어줌

import 'package:flutter/material.dart';

import '../../../constants/sizes.dart';

class FormButton extends StatelessWidget {
  const FormButton({
    super.key,
    required this.disabled,
  });

  final bool disabled;

  @override
  Widget build(BuildContext context) {
    return FractionallySizedBox(
      widthFactor: 1,

      // 색변화할 때 효과를 주기 위해 AnimatedContainer로 변경
      child: AnimatedContainer(
        padding: const EdgeInsets.symmetric(
          vertical: Sizes.size16,
        ),
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(Sizes.size5),
          color: disabled
              ? Colors.grey.shade300 // if
              : Theme.of(context).primaryColor, // else
        ),

        // AnimatedContainer에는 이게 필요
        duration: const Duration(microseconds: 500),

        // child에 별도로 이 위젯이 필요
        child: AnimatedDefaultTextStyle(
          duration: const Duration(microseconds: 500),
          // Duration과 함께 style도 여기로 옮겨야 함
          style: TextStyle(
            color: disabled ? Colors.grey : Colors.white,
            fontWeight: FontWeight.w600,
          ),
          child: const Text(
            "Next",
            textAlign: TextAlign.center,
          ),
        ),
      ),
    );
  }
}

 

 

@Controller을 initState에서 설정하고 사용한 다음 사용한 widget에서 dispose함수를 override하여 controller를 dispose해야 한다. 아니면 메모리 에러난다.

// TextField의 텍스트 변화를 감지하기 위한 이벤트 리스너
  @override
  void initState() {
    _usernameController.addListener(
      () {
        // print(_usernameController.text);
        setState(() {
          // 여기서 state를 저장해준다.
          _username = _usernameController.text;
        });
      },
    );
  }

// 위에 이벤트 리스너를 dispose해주지 않으면 비디오 재생등에서 메모리 부족으로 앱이 crash하는 걸 방지
  @override
  void dispose() {
    _usernameController.dispose();
    super.dispose();
  }

 

 

EmailScreen usernameScreen 거의 비슷. 연결시 GestureDetector 사용

email_screen.dart

import 'package:flutter/material.dart';
import 'package:tiktok_clone/constants/gaps.dart';
import 'package:tiktok_clone/constants/sizes.dart';

class EmailScreen extends StatefulWidget {
  const EmailScreen({super.key});

  @override
  State<EmailScreen> createState() => _EmailScreenState();
}

class _EmailScreenState extends State<EmailScreen> {
// Controller는 코드, 메서드 등으로 이 textField아 같은 위젯을 컨트롤할 수 있게 해준다.
  final TextEditingController _usernameController = TextEditingController();

// _username 정의하기
  String _username = "";

// TextField의 텍스트 변화를 감지하기 위한 이벤트 리스너
  @override
  void initState() {
    _usernameController.addListener(
      () {
        // print(_usernameController.text);
        setState(() {
          // 여기서 state를 저장해준다.
          _username = _usernameController.text;
        });
      },
    );
  }

// 위에 이벤트 리스너를 dispose해주지 않으면 비디오 재생등에서 메모리 부족으로 앱이 crash하는 걸 방지
  @override
  void dispose() {
    _usernameController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        title: const Text(
          "Sign up",
        ),
      ),
      // 좀더 텍스트 간격 정렬을 위해 padding
      body: Padding(
        padding: const EdgeInsets.symmetric(
          horizontal: Sizes.size36,
        ),
        child: Column(
          // 텍스트 정렬하기
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Gaps.v40,
            const Text(
              "What is your email?",
              style: TextStyle(
                fontSize: Sizes.size24,
                fontWeight: FontWeight.w700,
              ),
            ),
            Gaps.v16,
            // 유저가 문자를 입력할 수 있는 위젯
            TextField(
              // TextEditingController 함수 만들었으니 여기다 정의
              controller: _usernameController,
              decoration: InputDecoration(
                hintText: "Email",
                // 글자 밑 부분 border 회색으로 바꿔주려면 이 2개 필요
                enabledBorder: UnderlineInputBorder(
                  borderSide: BorderSide(
                    color: Colors.grey.shade400,
                  ),
                ),
                focusedBorder: UnderlineInputBorder(
                  borderSide: BorderSide(
                    color: Colors.grey.shade400,
                  ),
                ),
              ),
              // 핑크 커서 만들기
              cursorColor: Theme.of(context).primaryColor,
            ),
            Gaps.v16,

            // Next button 만들기
            FormButton(
              disabled: _username.isEmpty,
            ),
          ],
        ),
      ),
    );
  }
}

class FormButton extends StatelessWidget {
  const FormButton({
    super.key,
    required this.disabled,
  });

  final bool disabled;

  @override
  Widget build(BuildContext context) {
    return FractionallySizedBox(
      widthFactor: 1,

      // 색변화할 때 효과를 주기 위해 AnimatedContainer로 변경
      child: AnimatedContainer(
        padding: const EdgeInsets.symmetric(
          vertical: Sizes.size16,
        ),
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(Sizes.size5),
          color: disabled
              ? Colors.grey.shade300 // if
              : Theme.of(context).primaryColor, // else
        ),

        // AnimatedContainer에는 이게 필요
        duration: const Duration(microseconds: 1000),

        // child에 별도로 이 위젯이 필요
        child: AnimatedDefaultTextStyle(
          duration: const Duration(microseconds: 1000),
          // Duration과 함께 style도 여기로 옮겨야 함
          style: TextStyle(
            color: disabled ? Colors.grey : Colors.white,
            fontWeight: FontWeight.w600,
          ),
          child: const Text(
            "Next",
            textAlign: TextAlign.center,
          ),
        ),
      ),
    );
  }
}

마지막으로 next 버튼 누른 다음 행동을 함수 정의

 

// next 버튼 누른 다음 행동을 함수 정의
  void _onNextTap() {
    if (_username.isEmpty) return;
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (context) => const EmailScreen(),
      ),
    );
  }
  
  
  -------------------
  
                // 핑크 커서 만들기
              cursorColor: Theme.of(context).primaryColor,
            ),
            Gaps.v28,

  -------------------
            // Next button 만들기, // next 버튼 누른 다음 행동을 함수 정의
            GestureDetector(
              onTap: _onNextTap,
              child: FormButton(
                disabled: _username.isEmpty,
              ),
            ),

 

지금까지의 구조

728x90
반응형
Comments