개발/클론코딩

[Nomad Coders] Flutter로 UI 만들기

걍판자 2024. 1. 10. 20:42
반응형

Nomad Coders님의 Flutter 강의를 듣고 요약한 내용입니다. 링크는 아래와 같습니다.

해당 강의는 현재 무료로 이용할 수 있습니다.

 

https://nomadcoders.co/flutter-for-beginners

 

Flutter 로 웹툰 앱 만들기 – 노마드 코더 Nomad Coders

Flutter for Beginners

nomadcoders.co

 

 

Flutter를 처음 공부한다면 이전 글 요약부터 보고 오시는 걸 추천드립니다.

2024.01.09 - [개발] - [Nomad Coders] Flutter 를 위한 DART 문법 요약

 

[Nomad Coders] Flutter 를 위한 DART 문법 요약

이 글은 Nomad Coders님의 Dart 문법 강의를 복습을 위해 요약한 것입니다. 해당 강의는 가입하면 무료로 제공되는 강의라 누구나 쉽게 들을 수 있습니다. https://nomadcoders.co/dart-for-beginners Dart 시작하

juneforpay.tistory.com

 

 

Nomad Coders certifciation

 

Hello flutter

import 'package:flutter/material.dart';

void main(){
  runApp(App());
}
//App 클래스는 여기서 root 역할을 한다.

class App extends StatelessWidget{
  @override // 부모클래스에 있는 그러니까 원래 StatelessWidget에 있는 build 메소드를 가져다 쓴다.
  Widget build(BuildContext context){//context는 나중에 설명
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Hello flutter!'),
        ),
        body: Center(
          child: Text('Hello world!'),
        )
      ),
    );
  }
}
  • flutter 공식 사이트에서 각종 위젯들을 사용할 수 있다. 위젯 종류가 다양하여 원할 때 보고 사용하면 된다.
  • 위젯을 만드는 것을 클래스를 만드는 것과 같다. 단순히 클래스를 만드는 것은 아니고 extends를 이용해 3가지 위젯의 종류 중 하나를 상속받아야 위젯이 된다. 여기서는 StatelessWidget을 상속받았다.
  • 위젯을 만들고 싶다면 build 메소드가 필요하다.
  • 모든 것의 시초가 되는 root widget은 둘 중에 하나를 return 해야만 한다. material을 리턴하면 구글 형태로 , cupertino를 리턴하면 ios 형태로 위젯이 리턴된다. 어떤 family 스타일을 설정할지 보여주는 것이다.
  • MaterialApp의 프로퍼티에는 home이라는 위젯이 있다. 그래서 hello wolrd 라는 text 위젯을 부여한 것이다.
  • Scaffold는 화면 구조이다.
  • VS studio 에서는 클래스를 끝낼 때마다 쉼표를 찍는 게 보기 좋다.
  • 위젯에 위젯에 위젯을 쌓아간다.
  • 결론: 온세상이 다 위젯 타입으로 구성되어 있다. 위젯을 블록 쌓듯이 위젯에 위젯을 쌓아가며 앱을 만든다.

 

 

복습

  • 위젯은 build메소드를 구현해야 한다. 그리고 이 메서드는 또 다른 widget을 리턴해야 한다.
  • 자동완성 되는 위젯의 다양한 기능들을 보고 디자인적으로 다양하게 써먹을수 있다. background, elevation 등등..
  • 위젯을 리턴해야하는 위젯을 리턴해야 하는 위젯을 리턴해야 하는…

 

 

클래스 복습

class Player{
 String name = 'nico';//player 기본값

Player(this.name);
//아래에서 처음에 생성할때 괄호 안에 받는 걸로 그 인스턴스의 name 설정할수 있게함

}

void main(){
	var nico= Player("potato");
	// 앞에 new 생략 됨, 새로운 player 만듬
	nico.name //potato
}
class Player{
 String name = 'nico';//player 기본값

Player({requried this.name});
//아래에서 처음에 생성할때 괄호 안에 받는 걸로 그 인스턴스의 name 설정할수 있게함

}

void main(){
	var nico= Player(name: "potato");
	// 앞에 new 생략 됨, 새로운 player 만듬
	nico.name //potato
}
  • 위젯을 사용할때마다 클래스를 인스턴스화하는 것과 같다. 즉 모든 값 앞에 new를 붙이는 것과 같다. 다만, dart는 그럴 필요가 없어 쓰지 않는다.
  • 우리가 앞에서 만든 hello world 위젯들은 클래스를 순서 상관없이 :을 사용하는 named parameter 방식으로 넘기는 것 과 같다.
  • 중괄호를 쓰면 괄호 안을 :를 사용하는 named로 받아야 한다.
  • 자료형 앞에 ?를 붙이면 인스턴스에 해당 자료형 없어도 됨 선택이 되는 것 이는 위젯들도 마찬가지다. 마우스를 올리면 알 수 있다. 위젯의 필수 자료형이 빠져있다면 오류창이 뜰 것이다.
  • default를 이용하고 싶지 않다면, 변수의 자료형 앞에 required를 적어주면 무조건 값을 할당해야 한다는 것을 알기에 nullsafety 하게 쓸 수 있다. (이전 강의 요약 참고함)

 

UI Challenge

아래의 코드 적용시 나타나는 UI

Header, Developer Tools

import 'package:flutter/material.dart';

void main(){
  runApp(App());
}
//App 클래스는 여기서 root 역할을 한다.

class App extends StatelessWidget{
  @override // 부모클래스에 있는 그러니까 원래 StatelessWidget에 있는 build 메소드를 가져다 쓴다.
  Widget build(BuildContext context){//context는 나중에 설명
    return MaterialApp(
      home: Scaffold(
        backgroundColor: Color(0xFF181818),//배경색 약간 검정
        body: Padding( 
         padding: EdgeInsets.symmetric(horizontal:40),//가로방향 간격 40
         child:Column(//투명박스와 텍스트 박스로 구분하는 콜롬들
          children: [
            SizedBox(
             height: 80,//맨 위에 80만큼의 공백을 차지하는 투명 박스
            ),
            Row(//하나의 row 안에 즉 하나의 가로줄 안에 두가지의 콜롬 텍스트가 들어간다.
              mainAxisAlignment: MainAxisAlignment.end,//가로줄의 맨 끝으로 정렬한다.
              children:[//row의 유일한 children
                Column(children:[//column은 세로줄 정렬로 children을 가진다. children에 오는 순서대로 세로줄을 하나씩 쌓아간다.
                  Text('Hey, Selena',
                      style: TextStyle(
                       color: Colors.white,
                        fontSize: 28,
                        fontWeight: FontWeight.w800,//폰트 굵기 설정
                      ),
                       ),
                  Text('Welcome back',
                      style: TextStyle(
                       color: Colors.white.
                        withOpacity(0.8),//투명도 살짝 주기
                        fontSize:18),
                   ),//Text
                ],//Column(children:
               )// Column
              ],//children
            )//Row
          ],//children
         ),//child:Column
        ), //body: Padding 
      ),//home: Scaffold
    );//return MaterialApp
  }//Widget 
}//class App
  • a속성은 b값을 가진다 라고 할 때 a:b로 표기되는데 이때 a의 맨 앞은 소문자 b의 맨 앞은 대문자로 표현된다.
  • 속성들은 쉼표와 괄호로 잇는다.
  • mainAxisAlignment는 현재 같은 방향으로 정렬, crossAxisAlignment는 현재와 수직방형으로 정렬이다. 즉 column 안에서 main은 수직, cross는 수평, row 안에서 main은 수평, cross는 수직이다.
  • padding은 차지하는 공간에 더 많은 가장자리 여유를 준다. all 이면 padding을 모든 방향, only면 직접 수동 지정, symmetric은 수평, 수직 방향이다.
  • 에러가 난다면 어딘가에 콤마를 빼먹지 않았는지, 괄호를 닫지 않았는지 확인해 보자
  • 색깔은 Color, fromRGBO(24,24,24,1)과 같은 함수로 조정할 수도 있다.
  • 개발자 도구를 통해 코드를 바꾸지 않고도 편하게 화면을 미리 보기 할 수 있다. 또한 휴대폰 화면을 클릭해 위젯을 활성화할 수 있다. 자세한 사용 방법은 나중에 3.1강을 참고하자.
  • 위젯 안에 위젯으로 하위 위젯을 넣을 때 위젯이 복수개 가능하면 children, 단수개만 가능하면 child를 쓴다.

 

Button Section

아래의 코드 적용시 나타나는 UI

import 'package:flutter/material.dart';

void main(){
  runApp(App());
}
//App 클래스는 여기서 root 역할을 한다.

class App extends StatelessWidget{
  @override // 부모클래스에 있는 그러니까 원래 StatelessWidget에 있는 build 메소드를 가져다 쓴다.
  Widget build(BuildContext context){//context는 나중에 설명
    return MaterialApp(
      home: Scaffold(
        backgroundColor: Color(0xFF181818),
        body: Padding( 
         padding: EdgeInsets.symmetric(horizontal:40),
         child:Column(
           crossAxisAlignment: CrossAxisAlignment.start,//새로 추가되는 것들왼쪽 정렬
          children: [
            SizedBox(
             height: 80,
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.end,
              children:[
                Column(children:[
                  Text('Hey, Selena',
                      style: TextStyle(
                       color: Colors.white,
                        fontSize: 28,
                        fontWeight: FontWeight.w800,
                      ),
                       ),
                  Text('Welcome back',
                      style: TextStyle(
                       color: Colors.white.
                        withOpacity(0.8),
                        fontSize:18),
                   ),//Text
                ],//Column(children:
               )// Column
              ],//children
            ),//Row
            SizedBox(
            height: 120,
            ),//totalbalance과 같은 새로운 글자들 넣는데 그 사이의 간격
            Text(
            'Total Balance',
              style: TextStyle(fontSize: 22, color: Colors.white.withOpacity(0.8),
              )
            ),
            SizedBox(height: 5,),//totalbalance와 돈 사이의 간격
            Text(
            '\\$5 184 482',
              style: TextStyle(fontSize: 48, 
                               fontWeight: FontWeight.w600,
                               color: Colors.white,
              )
            ),
            SizedBox(height: 30,),//돈과 버튼 사이의 간격
            Row(//버튼들 들어갈 row
                children:[
                  Container(//단순한 상자
                    decoration: BoxDecoration(color:
                    Colors.amber,//그 상자를 꾸미는 코드
                    borderRadius: BorderRadius.circular(45),//상자 끝부분 둥글게
                    ),
                    child: Padding(
                      padding: EdgeInsets.symmetric
                      (vertical: 20, horizontal: 50,),//버튼과 글자사이 갭
                      child:Text('Transfer',
                            style:TextStyle(
                            fontsize: 22)),
                    )
                )],
            )
          ],//children
         ),//child:Column
        ), //body: Padding 
      ),//home: Scaffold
    );//return MaterialApp
  }//Widget 
}//class App
  • container을 사용해 버튼 같은 상자를 만들 수 있고 borderRadius의 circular로 모서리를 둥글게 만들 수 있다.

 

 

VSCode setting, Code Actions

  • const는 이미 알고 있는 value+ 변하지 않는 값일 때 앞에 붙이면 최적화가 가능하다.
  • vscode 파일을 건드리고 다시 시작하면, 이렇게 특정 부분에 const 붙이는 것을 자동화할 수 있으며, 또 다른 설정을 변경하면부 모와 자식이 누군지 알려주는 줄을 설정할 수 있다.
  • 전구 모양을 누르면 리팩토링을 제공한다. 이는 위젯중심의 다트 코드에서 새롭게 위젯이나 컨테이너 등으로 감싸야 할 때나 지울때 괄호, 쉼표 실수등을 줄여준다. 단축키를 통해서 전구를 호출할수도 있다. 아마 컨트롤 이랑 온점키일 것이다.
  • vs코드 사용 할때 3.5강 앞부분도 참고하기

 

 

Reusable Widgets

아래의 코드 적용시 나타나는 UI

import 'package:flutter/material.dart';

void main(){
  runApp(App());
}
//App 클래스는 여기서 root 역할을 한다.

class Button extends StatelessWidget{//extend 받아 Button이라는 클래스를 만들어 주었다.
  final String text;
  final Color bgColor;
  final Color textColor;
  
  const Button({
    super.key,//나중에 설명
    required this.text,
    required this.bgColor,
    required this.textColor, }); //받아야 하는 변수들
  
  @override
  Widget build(BuildContext context){
    return Container(
                    decoration: BoxDecoration(
                    color:bgColor,
                    borderRadius: BorderRadius.circular(45),
                    ),
                    child: Padding(
                      padding: const EdgeInsets.symmetric(
                      vertical: 20, 
                       horizontal: 50,
                      ),
                      child:Text(
                        text,
                        style:TextStyle(
                            color: textColor,
                            fontSize: 20,
                        ),
                      ),
                    ),
      );//항상 return 뒤에는 ;         
  }
  
}

class App extends StatelessWidget{
  @override // 부모클래스에 있는 그러니까 원래 StatelessWidget에 있는 build 메소드를 가져다 쓴다.
  Widget build(BuildContext context){//context는 나중에 설명
    return MaterialApp(
      home: Scaffold(
        backgroundColor: const Color(0xFF181818),
        body: Padding( 
         padding: const EdgeInsets.symmetric(horizontal:20),
         child:Column(
           crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const SizedBox(
             height: 80,
            ),
            const Row(
              mainAxisAlignment: MainAxisAlignment.end,
              children: [
                Column(children: [
                  Text(
                    'Hey, Selena',
                      style: TextStyle(
                       color: Colors.white,
                        fontSize: 28,
                        fontWeight: FontWeight.w800,
                      ),
                       ),
                  Text(
                    'Welcome back',
                      style: TextStyle(
                       color: Color.fromRGBO(255,255,255,0.8),
                        fontSize:18),
                   ),//Text
                ],//Column(children:
               )// Column
              ],//children
            ),//Row
            const SizedBox(
            height: 120,
            ),
            Text(
            'Total Balance',
              style: TextStyle(fontSize: 22, color: Colors.white.withOpacity(0.8),
              )
            ),
            const SizedBox(height: 5,),
            const Text(
            '\\$5 184 482',
              style: TextStyle(fontSize: 48, 
                               fontWeight: FontWeight.w600,
                               color: Colors.white,
              )
            ),
            const SizedBox(height: 30,),
            Row(
              mainAxisAlignment: MainAxisAlignment.
              spaceBetween,//둘 사이의 간격을 적당히 떨어뜨림
                children: const[
                Button(text: 'Transfer',
                bgColor: Color(0xFFF1B33B),
                textColor: Colors.black),
                  
                Button(text: 'Request',
                bgColor: Color(0xFF1F2123),
                textColor: Colors.white),
                  
               
              ],
            )
          ],//children
         ),//child:Column
        ), //body: Padding 
      ),//home: Scaffold
    );//return MaterialApp
  }//Widget 
}//class App
  • 전구의 extract widget을 이용하면 추출하여 재사용 가능한 위젯을 만들 수 있다.
  • 새로운 파일을 만들고 클래스를 만들어서 반복적인 위젯 작업을 피할 수 있다.
  • 설계도 역할을 하는 class는 고정된 값이 아니므로 const가 올 수 없다.

 

 

Cards

아래의 코드 적용시 나타나는 UI

 

import 'package:flutter/material.dart';

void main(){
  runApp(App());
}
//App 클래스는 여기서 root 역할을 한다.

class Button extends StatelessWidget{//extend 받아 Button이라는 클래스를 만들어 주었다.
  final String text;
  final Color bgColor;
  final Color textColor;
  
  const Button({
    super.key,//나중에 설명
    required this.text,
    required this.bgColor,
    required this.textColor, }); //받아야 하는 변수들
  
  @override
  Widget build(BuildContext context){
    return Container(
                    decoration: BoxDecoration(
                    color:bgColor,
                    borderRadius: BorderRadius.circular(45),
                    ),
                    child: Padding(
                      padding: const EdgeInsets.symmetric(
                      vertical: 20, 
                       horizontal: 50,
                      ),
                      child:Text(
                        text,
                        style:TextStyle(
                            color: textColor,
                            fontSize: 20,
                        ),
                      ),
                    ),
      );//항상 return 뒤에는 ;         
  }
  
}

class App extends StatelessWidget{
  @override // 부모클래스에 있는 그러니까 원래 StatelessWidget에 있는 build 메소드를 가져다 쓴다.
  Widget build(BuildContext context){//context는 나중에 설명
    return MaterialApp(
      home: Scaffold(
        backgroundColor: const Color(0xFF181818),
        body: Padding( 
         padding: const EdgeInsets.symmetric(horizontal:20),
         child:Column(
           crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const SizedBox(
             height: 80,
            ),
            const Row(
              mainAxisAlignment: MainAxisAlignment.end,
              children: [
                Column(children: [
                  Text(
                    'Hey, Selena',
                      style: TextStyle(
                       color: Colors.white,
                        fontSize: 28,
                        fontWeight: FontWeight.w800,
                      ),
                       ),
                  Text(
                    'Welcome back',
                      style: TextStyle(
                       color: Color.fromRGBO(255,255,255,0.8),
                        fontSize:18),
                   ),//Text
                ],//Column(children:
               )// Column
              ],//children
            ),//Row
            const SizedBox(
            height: 120,
            ),
            Text(
            'Total Balance',
              style: TextStyle(fontSize: 22, color: Colors.white.withOpacity(0.8),
              )
            ),
            const SizedBox(height: 5,),
            const Text(
            '\\$5 184 482',
              style: TextStyle(fontSize: 48, 
                               fontWeight: FontWeight.w600,
                               color: Colors.white,
              )
            ),
            const SizedBox(height: 30,),
            Row(
              mainAxisAlignment: MainAxisAlignment.
              spaceBetween,//둘 사이의 간격을 적당히 떨어뜨림
                children: const[
                Button(text: 'Transfer',
                bgColor: Color(0xFFF1B33B),
                textColor: Colors.black),
                  
                Button(text: 'Request',
                bgColor: Color(0xFF1F2123),
                textColor: Colors.white),
                  
               
              ],
            ),
          const SizedBox(
          height: 100,//간격
       ),
       Row(
         crossAxisAlignment:
         CrossAxisAlignment.end,
         mainAxisAlignment:
         MainAxisAlignment.spaceBetween,//세로 기준으로 열의 아래쪽에 가로 기준으로는 적당히 벌리며 정렬
         children: [
         Text(
           'Wallets',
         style: TextStyle(
          color: Colors.white,
          fontSize: 36,
          fontWeight: FontWeight.w600,
          )
         ),
         Text('View All',
         style: TextStyle(
          color: Colors.white.withOpacity(0.8),
          fontSize: 18,
          )),
       ],),
         const SizedBox(
            height:20,//아래 euro와 간격
          ),
        Container(
          decoration: BoxDecoration(
          color: Color(0xFF1f2123),
           borderRadius:BorderRadius.circular(25),
          ),
          child: Padding(
          padding: const EdgeInsets.all(30),//Euro 글씨의 padding
          child:Row(
          children:[
            Column(
              crossAxisAlignment:
              CrossAxisAlignment.start,
              children:[
                const Text('Euro',
                      style:TextStyle(color:
                      Colors.white,
                      fontSize:32,
                      fontWeight: FontWeight.w600,                
                      )
                          ),//9분
                const SizedBox(
                  height: 10,
                ),
                Row(
                  children:[
                    Text('6428',
                    style: TextStyle(
                      color: Colors.white,
                    fontSize:20,
                    )    
                        ),
                    const SizedBox(
                      width:5
                            ),
                    Text('EUR',
                        style: TextStyle(
                      color: Colors.white.withOpacity(0.8),
                    fontSize:20,
                    )),
                  ],
                )

              ],
            ),
          ],
          ),
         ),
        ),
          ],//children
         ),//child:Column
        ), //body: Padding 
      ),//home: Scaffold
    );//return MaterialApp
  }//Widget 
}//class App
  • 아래쪽 ui를 구성했다.

 

 

 

Icons and Transforms

아래의 코드 적용시 나타나는 UI

import 'package:flutter/material.dart';

void main(){
  runApp(App());
}
//App 클래스는 여기서 root 역할을 한다.

class Button extends StatelessWidget{//extend 받아 Button이라는 클래스를 만들어 주었다.
  final String text;
  final Color bgColor;
  final Color textColor;
  
  const Button({
    super.key,//나중에 설명
    required this.text,
    required this.bgColor,
    required this.textColor, }); //받아야 하는 변수들
  
  @override
  Widget build(BuildContext context){
    return Container(
                    decoration: BoxDecoration(
                    color:bgColor,
                    borderRadius: BorderRadius.circular(45),
                    ),
                    child: Padding(
                      padding: const EdgeInsets.symmetric(
                      vertical: 20, 
                       horizontal: 50,
                      ),
                      child:Text(
                        text,
                        style:TextStyle(
                            color: textColor,
                            fontSize: 20,
                        ),
                      ),
                    ),
      );//항상 return 뒤에는 ;         
  }
  
}

class App extends StatelessWidget{
  @override // 부모클래스에 있는 그러니까 원래 StatelessWidget에 있는 build 메소드를 가져다 쓴다.
  Widget build(BuildContext context){//context는 나중에 설명
    return MaterialApp(
      home: Scaffold(
        backgroundColor: const Color(0xFF181818),
        body: Padding( 
         padding: const EdgeInsets.symmetric(horizontal:20),
         child:Column(
           crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const SizedBox(
             height: 80,
            ),
            const Row(
              mainAxisAlignment: MainAxisAlignment.end,
              children: [
                Column(children: [
                  Text(
                    'Hey, Selena',
                      style: TextStyle(
                       color: Colors.white,
                        fontSize: 28,
                        fontWeight: FontWeight.w800,
                      ),
                       ),
                  Text(
                    'Welcome back',
                      style: TextStyle(
                       color: Color.fromRGBO(255,255,255,0.8),
                        fontSize:18),
                   ),//Text
                ],//Column(children:
               )// Column
              ],//children
            ),//Row
            const SizedBox(
            height: 120,
            ),
            Text(
            'Total Balance',
              style: TextStyle(fontSize: 22, color: Colors.white.withOpacity(0.8),
              )
            ),
            const SizedBox(height: 5,),
            const Text(
            '\\$5 184 482',
              style: TextStyle(fontSize: 48, 
                               fontWeight: FontWeight.w600,
                               color: Colors.white,
              )
            ),
            const SizedBox(height: 30,),
            Row(
              mainAxisAlignment: MainAxisAlignment.
              spaceBetween,//둘 사이의 간격을 적당히 떨어뜨림
                children: const[
                Button(text: 'Transfer',
                bgColor: Color(0xFFF1B33B),
                textColor: Colors.black),
                  
                Button(text: 'Request',
                bgColor: Color(0xFF1F2123),
                textColor: Colors.white),
                  
               
              ],
            ),
                   const SizedBox(
          height: 100,
       ),
       Row(
         crossAxisAlignment:
         CrossAxisAlignment.end,
         mainAxisAlignment:
         MainAxisAlignment.spaceBetween,
         children: [
         Text(
           'Wallets',
         style: TextStyle(
          color: Colors.white,
          fontSize: 36,
          fontWeight: FontWeight.w600,
          )
         ),
         Text('View All',
         style: TextStyle(
          color: Colors.white.withOpacity(0.8),
          fontSize: 18,
          )),
       ],),
         const SizedBox(
            height:20,
         ),
        Container(
          clipBehavior:Clip.hardEdge,
          decoration: BoxDecoration(
          color: Color(0xFF1f2123),
           borderRadius:BorderRadius.circular(25),
          ),
          child: Padding(
          padding: const EdgeInsets.all(30),
          child:Row(
            mainAxisAlignment:MainAxisAlignment.spaceBetween,// euro와 아이콘 떨어뜨리기
          children:[
            Column(
              crossAxisAlignment:
              CrossAxisAlignment.start,
              children:[
                const Text('Euro',
                      style:TextStyle(color:
                      Colors.white,
                      fontSize:32,
                      fontWeight: FontWeight.w600,                
                      )
                          ),//9분
                const SizedBox(
                  height: 10,
                ),
                Row(
                  children:[
                    Text('6428',
                    style: TextStyle(
                      color: Colors.white,
                    fontSize:20,
                    )    
                        ),
                    const SizedBox(
                      width:5
                            ),
                    Text('EUR',
                        style: TextStyle(
                      color: Colors.white.withOpacity(0.8),
                    fontSize:20,
                    )),
                  ],
                )

              ],
            ),
            Transform.scale(
              scale: 2.2,
              child: Transform.translate(
                offset: const Offset(-5,12),
                child: const Icon(
                  Icons.euro_rounded,
                  color: Colors.white,
                  size:88,
                )
              )
            )
  
          ],
          ),
         ),
        ),
          ],//children
         ),//child:Column
        ), //body: Padding 
      ),//home: Scaffold
    );//return MaterialApp
  }//Widget 
}//class App
  • 아래쪽 euro 마크를 아이콘을 이용해서 만들어 주었다.
  • clipBehavior은 크기를 벗어난 것을 어떻게 처리할지 결정하는 것인데 Clip.hardEdge로 정하면 크기를 벗어난 것을 잘라낸다. 그렇게 euro 아이콘을 잘랐다.
  • size를 크게 늘리면 그 주변을 감싸는 다른 요소들도 덩달아 커진다. 이때는 transform.scale을 이용해 주변크기에 상관없이 줄어들거나 늘어나게 할 수 있다.
  • Transform.scale은 Transform.translate를 child로 가진다.
  • Transform.translate는 offset을 무조건 가져야만 한다. offset은 (x, y) 좌표를 받아 위치를 이동시킨다.
  • Icons를 이용해 다양한 아이콘을 사용할 수 있다.

 

 

Reusable Cards

아래의 코드 적용시 나타나는 UI

import 'package:flutter/material.dart';

void main(){
  runApp(App());
}
//App 클래스는 여기서 root 역할을 한다.

class Button extends StatelessWidget{//extend 받아 Button이라는 클래스를 만들어 주었다.
  final String text;
  final Color bgColor;
  final Color textColor;
  
  const Button({
    super.key,//나중에 설명
    required this.text,
    required this.bgColor,
    required this.textColor, }); //받아야 하는 변수들
  
  @override
  Widget build(BuildContext context){
    return Container(
                    decoration: BoxDecoration(
                    color:bgColor,
                    borderRadius: BorderRadius.circular(45),
                    ),
                    child: Padding(
                      padding: const EdgeInsets.symmetric(
                      vertical: 20, 
                       horizontal: 50,
                      ),
                      child:Text(
                        text,
                        style:TextStyle(
                            color: textColor,
                            fontSize: 20,
                        ),
                      ),
                    ),
      );//항상 return 뒤에는 ;         
  }
  
}

class CurrencyCard extends StatelessWidget{
  final String name, code, amount;
  final IconData icon;
  final bool isInverted;
  final _blackColor = const Color(0xFF1f2123);
  
  const CurrencyCard({
    super.key,
    required this.name,
    required this.code,
    required this.amount,
    required this.icon,
    required this.isInverted,
    
  }); 
  
  @override
  Widget build(BuildContext context){
      return Container(
          clipBehavior:Clip.hardEdge,
          decoration: BoxDecoration(
          color: isInverted ? Colors.white: _blackColor,
           borderRadius:BorderRadius.circular(25),
          ),
          child: Padding(
          padding: const EdgeInsets.all(30),
          child:Row(
            mainAxisAlignment:MainAxisAlignment.spaceBetween,
          children:[
            Column(
              crossAxisAlignment:
              CrossAxisAlignment.start,
              children:[
                Text(name,
                      style:TextStyle(color: isInverted?
                      _blackColor                
                      :Colors.white,
                      fontSize:32,
                      fontWeight: FontWeight.w600,                
                      )
                          ),//9분
                const SizedBox(
                  height: 10,
                ),
                Row(
                  children:[
                    Text(amount,
                    style: TextStyle(
                      color: isInverted?
                      _blackColor                
                      :Colors.white,
                    fontSize:20,
                    )    
                        ),
                    SizedBox(
                      width:5
                            ),
                    Text(code,
                        style: TextStyle(
                      color: isInverted?
                      _blackColor                
                      :Colors.white.withOpacity(0.8),
                    fontSize:20,
                    )),
                  ],
                )

              ],
            ),
            Transform.scale(
              scale: 2.2,
              child: Transform.translate(
                offset: const Offset(-5,12),
                child: Icon(
                  icon,
                  color:isInverted?
                      _blackColor                
                      :Colors.white,
                  size:88,
                )
              )
            )
  
          ],
          ),
         ),
        );
    
  }
  
}

class App extends StatelessWidget{
  @override // 부모클래스에 있는 그러니까 원래 StatelessWidget에 있는 build 메소드를 가져다 쓴다.
  Widget build(BuildContext context){//context는 나중에 설명
    return MaterialApp(
      home: Scaffold(
        backgroundColor: const Color(0xFF181818),
        body: SingleChildScrollView( 
         child:Padding( 
         padding: const EdgeInsets.symmetric(horizontal:20),
         child:Column(
           crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const SizedBox(
             height: 80,
            ),
            const Row(
              mainAxisAlignment: MainAxisAlignment.end,
              children: [
                Column(children: [
                  Text(
                    'Hey, Selena',
                      style: TextStyle(
                       color: Colors.white,
                        fontSize: 28,
                        fontWeight: FontWeight.w800,
                      ),
                       ),
                  Text(
                    'Welcome back',
                      style: TextStyle(
                       color: Color.fromRGBO(255,255,255,0.8),
                        fontSize:18),
                   ),//Text
                ],//Column(children:
               )// Column
              ],//children
            ),//Row
            const SizedBox(
            height: 70,
            ),
            Text(
            'Total Balance',
              style: TextStyle(fontSize: 22, color: Colors.white.withOpacity(0.8),
              )
            ),
            const SizedBox(height: 5,),
            const Text(
            '\\$5 184 482',
              style: TextStyle(fontSize: 48, 
                               fontWeight: FontWeight.w600,
                               color: Colors.white,
              )
            ),
            const SizedBox(height: 30,),
            Row(
              mainAxisAlignment: MainAxisAlignment.
              spaceBetween,//둘 사이의 간격을 적당히 떨어뜨림
                children: const[
                Button(text: 'Transfer',
                bgColor: Color(0xFFF1B33B),
                textColor: Colors.black),
                  
                Button(text: 'Request',
                bgColor: Color(0xFF1F2123),
                textColor: Colors.white),
                  
               
              ],
            ),
                   const SizedBox(
          height: 100,
       ),
       Row(
         crossAxisAlignment:
         CrossAxisAlignment.end,
         mainAxisAlignment:
         MainAxisAlignment.spaceBetween,
         children: [
         Text(
           'Wallets',
         style: TextStyle(
          color: Colors.white,
          fontSize: 36,
          fontWeight: FontWeight.w600,
          )
         ),
         Text('View All',
         style: TextStyle(
          color: Colors.white.withOpacity(0.8),
          fontSize: 18,
          )),
       ],),
         const SizedBox(
            height:20,
         ),
            const CurrencyCard(
              name:'Euro',
              code: 'EUR',
              amount: '6 428',
              icon: Icons.euro_rounded,
              isInverted: false,
            ),
            Transform.translate(
              offset: const Offset(0,-20),
              child: const CurrencyCard(
                name:'BitCoin',
                code: 'BTC',
                amount: '9 785',
                icon: Icons.currency_bitcoin,
                isInverted: true,
              ),
            ),
            Transform.translate(
            offset: const Offset(0,-40),
            child: const CurrencyCard(
              name:'Dollar',
              code: 'USD',
              amount: '428',
              icon: Icons.attach_money_outlined,
              isInverted: false,
            ),
           ),
            

          ],//children
         ),//child:Column
        ), //body: Padding
        ),
      ),//home: Scaffold
    );//return MaterialApp
  }//Widget 
}//class App
  • icon 같은 속성도 속성 타입을 class 안에서 정해주고 객체에 맞게 Icons. 아이콘이름을 설정해 줌으로써 객체를 생성할 수 있다.
  • wallets 밑의 카드들을 객체화하였다.
  • transform.translate의 offset은 원래 그리드로 정해진 위치에서 다른 오브젝트들을 무시하고 이동시킬 수 있다.
  • padding 이전 body에 SingleChildScrollView 위젯을 넣음으로써 오버플로우 된 내용은 스크롤 처리할 수 있다.
  • isinverted라는 bool을 받아 색상 반전 역시 프로퍼티화 하였다.
  • 변수 앞에 _를 붙여 private로 바꿔줄 수 있다.

 

 

Code Challenge

import 'package:flutter/material.dart';

void main(){
  runApp(App());
}
//App 클래스는 여기서 root 역할을 한다.

class Button extends StatelessWidget{//extend 받아 Button이라는 클래스를 만들어 주었다.
  final String text;
  final Color bgColor;
  final Color textColor;
  
  const Button({
    super.key,//나중에 설명
    required this.text,
    required this.bgColor,
    required this.textColor, }); //받아야 하는 변수들
  
  @override
  Widget build(BuildContext context){
    return Container(
                    decoration: BoxDecoration(
                    color:bgColor,
                    borderRadius: BorderRadius.circular(45),
                    ),
                    child: Padding(
                      padding: const EdgeInsets.symmetric(
                      vertical: 20, 
                       horizontal: 50,
                      ),
                      child:Text(
                        text,
                        style:TextStyle(
                            color: textColor,
                            fontSize: 20,
                        ),
                      ),
                    ),
      );//항상 return 뒤에는 ;         
  }
  
}

class CurrencyCard extends StatelessWidget{
  final String name, code, amount;
  final IconData icon;
  final bool isInverted;
  final _blackColor = const Color(0xFF1f2123);
  final int order;
  
  const CurrencyCard({
    super.key,
    required this.name,
    required this.code,
    required this.amount,
    required this.icon,
    required this.isInverted,
    required this.order,
    
  }); 
  
  @override
  Widget build(BuildContext context){
      return 
      Transform.translate(
          offset: Offset(0,-20*(order-1)),
          child:Container(
          clipBehavior:Clip.hardEdge,
          decoration: BoxDecoration(
          color: isInverted ? Colors.white: _blackColor,
           borderRadius:BorderRadius.circular(25),
          ),
          child: Padding(
          padding: const EdgeInsets.all(30),
          child:Row(
            mainAxisAlignment:MainAxisAlignment.spaceBetween,
          children:[
            Column(
              crossAxisAlignment:
              CrossAxisAlignment.start,
              children:[
                Text(name,
                      style:TextStyle(color: isInverted?
                      _blackColor                
                      :Colors.white,
                      fontSize:32,
                      fontWeight: FontWeight.w600,                
                      )
                          ),//9분
                const SizedBox(
                  height: 10,
                ),
                Row(
                  children:[
                    Text(amount,
                    style: TextStyle(
                      color: isInverted?
                      _blackColor                
                      :Colors.white,
                    fontSize:20,
                    )    
                        ),
                    const SizedBox(
                      width:5
                            ),
                    Text(code,
                        style: TextStyle(
                      color: isInverted?
                      _blackColor                
                      :Colors.white.withOpacity(0.8),
                    fontSize:20,
                    )),
                  ],
                )

              ],
            ),
            Transform.scale(
              scale: 2.2,
              child: Transform.translate(
                offset: const Offset(-5,12),
                child: Icon(
                  icon,
                  color:isInverted?
                      _blackColor                
                      :Colors.white,
                  size:88,
                )
              )
            )
  
          ],
          ),
         ),
         ),
        );
    
  }
  
}

class App extends StatelessWidget{
  @override // 부모클래스에 있는 그러니까 원래 StatelessWidget에 있는 build 메소드를 가져다 쓴다.
  Widget build(BuildContext context){//context는 나중에 설명
    return MaterialApp(
      home: Scaffold(
        backgroundColor: const Color(0xFF181818),
        body: SingleChildScrollView( 
         child:Padding( 
         padding: const EdgeInsets.symmetric(horizontal:20),
         child:Column(
           crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const SizedBox(
             height: 80,
            ),
            const Row(
              mainAxisAlignment: MainAxisAlignment.end,
              children: [
                Column(children: [
                  Text(
                    'Hey, Selena',
                      style: TextStyle(
                       color: Colors.white,
                        fontSize: 28,
                        fontWeight: FontWeight.w800,
                      ),
                       ),
                  Text(
                    'Welcome back',
                      style: TextStyle(
                       color: Color.fromRGBO(255,255,255,0.8),
                        fontSize:18),
                   ),//Text
                ],//Column(children:
               )// Column
              ],//children
            ),//Row
            const SizedBox(
            height: 70,
            ),
            Text(
            'Total Balance',
              style: TextStyle(fontSize: 22, color: Colors.white.withOpacity(0.8),
              )
            ),
            const SizedBox(height: 5,),
            const Text(
            '\\$5 184 482',
              style: TextStyle(fontSize: 48, 
                               fontWeight: FontWeight.w600,
                               color: Colors.white,
              )
            ),
            const SizedBox(height: 30,),
            const Row(
              mainAxisAlignment: MainAxisAlignment.
              spaceBetween,//둘 사이의 간격을 적당히 떨어뜨림
                children: [
                Button(text: 'Transfer',
                bgColor: Color(0xFFF1B33B),
                textColor: Colors.black),
                  
                Button(text: 'Request',
                bgColor: Color(0xFF1F2123),
                textColor: Colors.white),
                  
               
              ],
            ),
                   const SizedBox(
          height: 100,
       ),
       Row(
         crossAxisAlignment:
         CrossAxisAlignment.end,
         mainAxisAlignment:
         MainAxisAlignment.spaceBetween,
         children: [
         const Text(
           'Wallets',
         style: TextStyle(
          color: Colors.white,
          fontSize: 36,
          fontWeight: FontWeight.w600,
          )
         ),
         Text('View All',
         style: TextStyle(
          color: Colors.white.withOpacity(0.8),
          fontSize: 18,
          )),
       ],),
         const SizedBox(
            height:20,
         ),
            const CurrencyCard(
              name:'Euro',
              code: 'EUR',
              amount: '6 428',
              icon: Icons.euro_rounded,
              isInverted: false,
              order:1,
            ),
            const CurrencyCard(
                name:'BitCoin',
                code: 'BTC',
                amount: '9 785',
                icon: Icons.currency_bitcoin,
                isInverted: true,
                order:2,
              
            ),
              const CurrencyCard(
              name:'Dollar',
              code: 'USD',
              amount: '428',
              icon: Icons.attach_money_outlined,
              isInverted: false,
              order:3,
            ),

          ],//children
         ),//child:Column
        ), //body: Padding
        ),
      ),//home: Scaffold
    );//return MaterialApp
  }//Widget 
}//class App
  • 기존의 코드에서 transform.translate이 거추장스러우니 코드를 줄이고 클래스화 시키는 발전과제다.
  • 기존의 코드에서 transform.translate까지 위젯에 포함시키고, offset은 order 1,2,3으로 받아 계산하였다
반응형