Flutter

[Flutter] login app 만들기

ekkkang 2025. 1. 13. 12:35
테스트 코드 

 

import 'package:flutter/material.dart';

// 텍스트 필드와 form 위젯 만들어 보기
// 테스트 코드 작업
void main() => runApp(const MyApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SafeArea(
        child: Scaffold(
          body: FormPage(),
        ),
      ),
    );
  }
}

////////////////////////////////////////////////////
// 변경 가능한 변수를 --> 상태가 있는 위젯이다.
class FormPage extends StatefulWidget {
  const FormPage({super.key});

  // 강한 소유의 관계
  @override
  State<FormPage> createState() => _FormPageState();
}

class _FormPageState extends State<FormPage> {
  String _username = '';
  String _password = '';

  // 타입추론
  // 폼 상태를 추척하기 위해 관리하는 고유키를 하나 선언한다.
  // 즉 폼에 상태에 접근할 수 있다. (이 키를 가지고)
  final _formKey = GlobalKey<FormState>();

  // 화면을 그리는 메서드
  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Column(
        children: [
          TextFormField(
            keyboardType: TextInputType.text,
            validator: (value) {
              if (value == null || value.isEmpty) {
                return 'username 을 입력하시오';
              }
              print('텍스트 필드 사용자 이름 ');
              return null;
            },
            onSaved: (value) {
              print('사용자 이름 필드 onSaved 호출됨');
              _username = value!;
            },
          ),
          SizedBox(height: 24.0),
          TextFormField(
            // 패스워드 받을 때 사용하는 속성
            obscureText: true,
            validator: (value) {
              if (value == null || value.isEmpty) {
                // 유효성을 통과 못했으면 텍스트 필드 아래에 친절하게 알려 줘
                return '패스워드를 입력 하시오';
              }
              return null;
            },
            onSaved: (value) {
              print('사용자 비밀번호 필드 onSaved 호출됨');
              _password = value!;
            },
          ),
          SizedBox(height: 24.0),
          ElevatedButton(
            onPressed: () {
              // 유효성 검사 호출
              if (_formKey.currentState!.validate()) {
                // true 일 경우 들어 옴
                print('true을 반환');
                // 저장 메서드 호출
                _formKey.currentState!.save();
                // 통신 하는 코드를 ---> 서버측으로데이터를 보냄
                // 멤버 변수를 활용해서
              }
            },
            child: Text('Login'),
          ),
        ],
      ),
    );
  }
}

////////////////////////////////////////////////////

 

 

이미지 다운로드 해주세요

 

 

pubspec.yaml 파일
name: flutter_login_app
description: "A new Flutter project."
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev

# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1

environment:
  sdk: ^3.5.3

# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
  flutter:
    sdk: flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.8
  flutter_svg: ^2.0.17

dev_dependencies:
  flutter_test:
    sdk: flutter

  # The "flutter_lints" package below contains a set of recommended lints to
  # encourage good coding practices. The lint set provided by the package is
  # activated in the `analysis_options.yaml` file located at the root of your
  # package. See that file for information about deactivating specific lint
  # rules and activating additional ones.
  flutter_lints: ^4.0.0

# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

# The following section is specific to Flutter packages.
flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  # To add assets to your application, add an assets section, like this:
  assets:
    - assets/logo.svg
  #   - images/a_dot_ham.jpeg

  # An image asset can refer to one or more resolution-specific "variants", see
  # https://flutter.dev/to/resolution-aware-images

  # For details regarding adding assets from package dependencies, see
  # https://flutter.dev/to/asset-from-package

  # To add custom fonts to your application, add a fonts section here,
  # in this "flutter" section. Each entry in this list should have a
  # "family" key with the font family name, and a "fonts" key with a
  # list giving the asset and other descriptors for the font. For
  # example:
  # fonts:
  #   - family: Schyler
  #     fonts:
  #       - asset: fonts/Schyler-Regular.ttf
  #       - asset: fonts/Schyler-Italic.ttf
  #         style: italic
  #   - family: Trajan Pro
  #     fonts:
  #       - asset: fonts/TrajanPro.ttf
  #       - asset: fonts/TrajanPro_Bold.ttf
  #         weight: 700
  #
  # For details regarding fonts from package dependencies,
  # see https://flutter.dev/to/font-from-package

 

 

size.dart 파일 만들기 
// 전역 상수 값들을 모아둘 파일 (사이즈와 관련된 상수)
// UI 에서 자주 사용하는 (여백, 크기, 패딩)을 상수로 정의해 두면 여러가지
// 이점이 있을 수 있다.
// 아래 상수들을 프로그램이 시작될 때부터 종료 될때 까지 어디서든지
// 동일한 값을 참조 한다.

// const 정의된 변수는 static 메모리 영역에 들어 간다.
const double smallGap = 5.0;
const double mediumGap = 10.0;
const double largeGap = 20.0;
const double xlargeGap = 100.0;

 

 

custom_form_text_field.dart
import 'package:flutter/material.dart';

class CustomTextFormField extends StatelessWidget {
  final String label;
  final FormFieldSetter<String>? onSaved;

  const CustomTextFormField(this.label, this.onSaved, {super.key});

  @override
  Widget build(BuildContext context) {
    return TextFormField(
      decoration: InputDecoration(
        labelText: label,
        border: OutlineInputBorder(),
      ),
      validator: (value) {
        if (value == null || value.isEmpty) {
          return '${label} 을 입력하세요';
        }
        return null;
      },
      // save 호출 시 데이터 저장
      onSaved: onSaved,
    );
  }
}

 

 

custom_form.dart 
import 'package:flutter/material.dart';
import 'package:flutter_login_app/components/custom_text_form_field.dart';
import 'package:flutter_login_app/size.dart';

class CustomForm extends StatelessWidget {
  // 1. 글로벌 키 선언 - 폼 상태를 관리하기 위한 Key 이다.
  final _formKey = GlobalKey<FormState>();
  // 2. 사용자가 입력을한 데이터를 저장하는 멤버 변수 선언
  String _email = '';
  String _password = '';

  CustomForm({super.key});

  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Column(
        children: [
          CustomTextFormField(
            'Email',
            (value) {
              _email = value ?? '';
            },
          ),
          SizedBox(height: mediumGap),
          CustomTextFormField(
            'Password',
            (value) {
              _password = value ?? '';
            },
          ),
          SizedBox(height: largeGap),
          TextButton(
            onPressed: () {
              // 유효성 검사
              if (_formKey.currentState!.validate()) {
                // 유효성 검사가 다 통과 하면 아래 로직 호출
                // save() 메서드를 호출 해야 되고
                // 통신 요청
                // 로깅 확인
                print('Email : ${_email}');
                print('Password : ${_password}');
              }
            },
            child: Text('Login'),
          )
        ],
      ),
    );
  }
}

 

 

main.dart
import 'package:flutter/material.dart';
import 'package:flutter_login_app/custom_form.dart';
import 'package:flutter_login_app/size.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: SafeArea(
        child: Scaffold(
          body: LoginPage(),
        ),
      ),
    );
  }
}

// 페이지
class LoginPage extends StatelessWidget {
  const LoginPage({super.key});

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: [
        SizedBox(height: largeGap),
        CustomForm(),
      ],
    );
  }
}

 

 

logo.dart
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';

class Logo extends StatelessWidget {
  const Logo(this.title, {super.key});

  final String title;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        SvgPicture.asset(
          height: 70,
          width: 70,
          'assets/logo.svg',
        ),
        Text(
          title,
          style: TextStyle(
            fontSize: 40,
            fontWeight: FontWeight.bold,
          ),
        )
      ],
    );
  }
}

 


import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return const Placeholder();
  }
}

 

import 'package:flutter/material.dart';
import 'package:flutter_login_app/components/custom_form.dart';
import 'package:flutter_login_app/components/logo.dart';
import 'package:flutter_login_app/size.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          SizedBox(height: xlargeGap),
          Logo('Login'),
          SizedBox(height: largeGap),
          CustomForm(),
        ],
      ),
    );
  }
}

 

import 'package:flutter/material.dart';
import 'package:flutter_login_app/components/custom_form.dart';
import 'package:flutter_login_app/components/logo.dart';
import 'package:flutter_login_app/pages/login_page.dart';
import 'package:flutter_login_app/size.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: SafeArea(
        child: Scaffold(
          body: LoginPage(),
        ),
      ),
    );
  }
}

'Flutter' 카테고리의 다른 글

[Flutter] 콜백 함수의 이해  (1) 2025.01.14
[Flutter] ListView_GridView_PageView  (1) 2025.01.13
[Flutter] profile app 만들기  (0) 2025.01.08
[Flutter] List_ 사용법  (0) 2025.01.08
[Flutter] 플러터 기본기 다지기 - 1  (0) 2025.01.08