날이 많이 추워졌다..

긴팔 긴바지 입었지만.

슬리퍼를 신고나왔더니 발꾸락이 춥다!

 

플러터를 공부하면서 느끼는 거지만

최고의 선생님은 구글인거같다..

검색하면 다나온다 정말..

물론 영어로 검색해야하기에 영어를 잘하면 좋겠지만

비슷하게 검색하더라도 잘나온다.

 

지난번 시간에 만든 AppBar에 있는 메뉴버튼을 누르면 좌측에서 Drawer menu가 나오게 구현하려고한다.

우선 복습할겸 새프로젝트를 시작해 기본틀부터 다시 만들었다...

    return Scaffold(
      appBar: AppBar(
        title: Text('Drawer App'),
        centerTitle: true,
        actions:[
          IconButton(onPressed:(){}, icon: Icon(Icons.shopping_cart, size: 25,),),
          IconButton(onPressed: (){}, icon: Icon(Icons.search_rounded, size: 25,),),
        ],
      ),
    );

title Text를 Center로 묶어주는것과

centertitles을 해주는것은 좀 다르다..

후자의 경우가 정가운데로 정렬해주기에 사용했다.

 

맨 왼쪽 menu 아이콘은 Drawer를 구현하면 자동으로 생성되기에 삭제해줘야한다.

 

 

 

 

 

 

drawer: Drawer(
  child: ListView(
    padding: EdgeInsets.zero,
    children:[
      UserAccountsDrawerHeader(
        currentAccountPicture: CircleAvatar(
          backgroundImage: AssetImage('assets/a.png'),
          backgroundColor: Colors.white,
        ),
        accountName: Text('monster'),
        accountEmail: Text('tngudzzzz@naver.com'),
        onDetailsPressed: () {},
        decoration: BoxDecoration(
          color: Colors.red[200],
          borderRadius: BorderRadius.only(
            bottomRight: Radius.circular(40.0),
            bottomLeft: Radius.circular(40.0),
          )
          )
        ),
    ],
  ),
),

appbar를 벗어나

drawer를 만들어준다.

-ListView

   - UserAccountsDrawerHeader

       - currentAccountPicture

       - accountName (필수)

       - accountEmail (필수)

이런 구조로 되어있다.

Image assets 하는방법은

1. 프로젝트 디렉토리에 새로운 디렉토리를 만들고 그안에 이미지 파일을 넣어준다.

2.

pubspec.yaml에 들어가 해당 부분에 이미지 경로를 넣어준다.

위 에시는 assets라는 새로운 디렉토리를 만들어 안에 a와 b라는 이미지 파일을 넣어줬다.

여기서 주의해야할 점은 띄어쓰기가 중요하다.

빨간색 부분은 띄어쓰기 2번 or 탭 1번

노란색 부분은 띄어쓰기 4번 or 탭 2번

이렇게 맞춰줘야 인식이 가능하다.

본문에서의 호출법은 AssetImage('assets/a.png') 이다.

onDetailsPressed: () {}

이 부분은 사진에 보이는 아랫 화살표 표시이다.

 

이제 아래에 Listtile을 추가해보자

사진을 보면 home, settings, Q&A  총 3개의 Listtile로 구성되어있다.

하나의 코드만 보자면

    ListTile(
      leading: Icon(Icons.home, color: Colors.grey[850],),
      title: Text('home'),
      onTap: () {
        print('onTap is Tap');
      },
      trailing: Icon(Icons.add),
    ),

leading은 appbar에서도 사용했듯

처음으로 오는 아이콘을 뜻한다.

가장오른쪽에 붙는 + 아이콘은 trailing으로 구현하면된다.

이번 시간에는 버튼을 학습했다.

단순한 버튼은 아니고 클릭하면 메시지가 뜨게 하는 버튼이다.

Material은 기본적으로 snackbar를 제공하여 메시지를 띄울수있지만 안이쁘다...

난 이쁜걸 원한다...

바로 Toast라는 것이다.

먼저 설치 방법부터 알아보자

1. pubspec.yaml 수정

프로젝트 파일중 pubspec.yaml에 들어가

fluttertoast: ^8.0.9를 넣어준다.

그 다음 위쪽 Pub get을 눌러 load 해준다.

 

2, main.dart 수정

import 'package:fluttertoast/fluttertoast.dart';

맨 윗줄에 해당 문구 삽입

 

이렇게 진행 하면 사용준비는 완료이다.

 

이번 시간에 만들 예제이다.

appbar - 버튼 3개 텍스트 1개

body - 텍스트 1개 버튼2개

로 구성되어있다.

 

appbar에 있는 버튼은 Toast 기본 설정으로 띄울것이며

body에 있는 Custom Button은 Toast-Custom을 이용

Success Button는 motion-toast를 사용할것이다.

*motion-toast는 별도의 설정이 필요하다... 검색 ㄱㄱ

 

appBar: AppBar(
  title: Center(child: Text("appBar & Toast Test")),
  elevation: 0.0,
  leading: IconButton(
      icon: Icon(Icons.menu),
      onPressed: () {
        showToast('이것은 메뉴입니다.');
      }),
  actions: [
    IconButton(
      icon: Icon(Icons.shopping_cart),
      onPressed: () {
        showToast('이것은 장바구니입니다.');
      },
    ),
    IconButton(
      icon: Icon(Icons.search_rounded),
      onPressed: () {
        showToast('이것은 검색입니다.');
      },
    ),
  ],
),

 

leading - appbar 맨 왼쪽에 자리잡는 위젯이다.

action - appbar 맨 오른쪽에 자리잡는 위젯이다.

 

body: Container(
  height: double.infinity,
  width: double.infinity,
  child: Column(
    mainAxisAlignment: MainAxisAlignment.center,
    crossAxisAlignment: CrossAxisAlignment.center,
    children: [
      const Text("Toast 메시지 예제", style: TextStyle(fontSize: 20,),
      ),
      const SizedBox(height: 10,),
      Container(width: 200, height: 50,
        child: ElevatedButton(
          onPressed: () {_showToast();},
          child: const Text("Custom Button", style: TextStyle(fontSize: 20,),),
        ),
      ),
      const SizedBox(height: 10,),
      Container(width: 200, height: 50,
        child: ElevatedButton(
          onPressed: () {_displaySuccessToast(context);},
          child: const Text("Success Toast", style: TextStyle(fontSize: 20,),),
        ),
      ),
height: double.infinity,
width: double.infinity,

mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,

이 두가지는 항상 쓰려고한다. 왜냐하면 위젯들이 센터로 자리잡을 수 있게 한다.

중간중간 SizeBox를 통해 위젯마다 공간을 주었다.

 

이제 Toast 부분을 살펴보자

 

void showToast(String msg2) {
  Fluttertoast.showToast(
    msg: msg2,
    toastLength: Toast.LENGTH_SHORT,
    gravity: ToastGravity.BOTTOM,
    backgroundColor: Colors.red,
    textColor: Colors.white,
  );
}
onPressed: () {
  showToast('이것은 장바구니입니다.');
},

이것은 appbar에 들어간 Toast 기본 버튼이다.

Fluttertoast.showToast를 이용하면 쉽게 구현가능하다.

두번째 코드는 호출 코드이다.

코드와 같이 String을 넣어줄수있다.

 

late FToast fToast;

@override
void initState() {
  super.initState();
  fToast = FToast();
  fToast.init(context);
}
_showToast() {
  Widget toast = Container(
    padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
    decoration: BoxDecoration(
      borderRadius: BorderRadius.circular(25.0),
      color: Colors.redAccent,
    ),
    child: Row(
      mainAxisSize: MainAxisSize.min,
      children: const [
        Icon(Icons.check, color: Colors.white,),
        SizedBox(
          width: 10.0,
        ),
        Text('이것은 커스텀 버튼입니다.', style: TextStyle(color: Colors.white,),),
      ],
    ),
  );

  fToast.showToast(
    child: toast,
    gravity: ToastGravity.BOTTOM,
    toastDuration: Duration(seconds: 1),
  );
}
onPressed: () {
_showToast();
},

Toast를 커스텀 할 수 있는 코드이다.

기본으로 해도 충분히 깔끔하게 나오지만 좀 더 아쉬우면 해당 기능으로 구현해보자.

여럿 값을 만져주면서 어떤 부분이 어떻게 변경되는지 쉽게 확인가능하다.

 

import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:motion_toast/motion_toast.dart';

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

class appBar extends StatelessWidget {
  const appBar({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'appBar & Toast',
      theme: ThemeData(primarySwatch: Colors.lightGreen),
      home: firstpage(),
    );
  }
}

class firstpage extends StatefulWidget {
  const firstpage({Key? key}) : super(key: key);

  @override
  State<firstpage> createState() => _firstpageState();
}

class _firstpageState extends State<firstpage> {

  late FToast fToast;

  @override
  void initState() {
    super.initState();
    fToast = FToast();
    fToast.init(context);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: const Color(0xFF69abce),
        appBar: AppBar(
          title: Center(child: Text("appBar & Toast Test")),
          elevation: 0.0,
          leading: IconButton(
              icon: Icon(Icons.menu),
              onPressed: () {
                showToast('이것은 메뉴입니다.');
              }),
          actions: [
            IconButton(
              icon: Icon(Icons.shopping_cart),
              onPressed: () {
                showToast('이것은 장바구니입니다.');
              },
            ),
            IconButton(
              icon: Icon(Icons.search_rounded),
              onPressed: () {
                showToast('이것은 검색입니다.');
              },
            ),
          ],
        ),
        body: Container(
          height: double.infinity,
          width: double.infinity,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              const Text("Toast 메시지 예제", style: TextStyle(fontSize: 20,),
              ),
              const SizedBox(height: 10,),
              Container(width: 200, height: 50,
                child: ElevatedButton(
                  onPressed: () {_showToast();},
                  child: const Text("Custom Button", style: TextStyle(fontSize: 20,),),
                ),
              ),
              const SizedBox(height: 10,),
              Container(width: 200, height: 50,
                child: ElevatedButton(
                  onPressed: () {_displaySuccessToast(context);},
                  child: const Text("Success Toast", style: TextStyle(fontSize: 20,),),
                ),
              ),
            ],
          ),
        ));
  }

  void showToast(String msg2) {
    Fluttertoast.showToast(
      msg: msg2,
      toastLength: Toast.LENGTH_SHORT,
      gravity: ToastGravity.BOTTOM,
      backgroundColor: Colors.red,
      textColor: Colors.white,
    );
  }

  void _displaySuccessToast(context) {
    MotionToast.success(
      title: Text("Success"),
      description: Text("This is Success Toast"),
    ).show(context);
  }

  _showToast() {
    Widget toast = Container(
      padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(25.0),
        color: Colors.redAccent,
      ),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: const [
          Icon(Icons.check, color: Colors.white,),
          SizedBox(
            width: 10.0,
          ),
          Text('이것은 커스텀 버튼입니다.', style: TextStyle(color: Colors.white,),),
        ],
      ),
    );

    fToast.showToast(
      child: toast,
      gravity: ToastGravity.BOTTOM,
      toastDuration: Duration(seconds: 1),
    );
  }
}

 

오늘 진행한 화면 페이지

보이는 사진을 구역별로 나눠 코드를 보여주고 설명하도록 하겠다..

우선적으로 이번 예제에선 데이터에 변화가 없기에 Stateless Widget만 사용한다.

material을 import 시켜주고

기본적인 위젯 코드를 짜주자 외울필요없이 stl만 치면 나온다. 

위 코드가 항상 시작할때 필요한 기본 코드이다.

맨위 appbar이다.

appbar를 보면 배경색이 있는데 보통강의를 보면 Colors.red 처럼 지정된 색을 사용하였지만 나는 hex코드를 이용했다.

hex코드는 Color(0xFF-------) 이렇게 사용하면 된다. 3번째줄의 색 코드는 앱 전체 배경색이고 

appbar이 배경색 코드는 아랫쪽이다.

title는 센터로 한번 감싸주어 Text와 backgroundColor 두개 child를 갖고있다.

아래 elevation은 appbar와 body의 경계 음영이다. 숫자를 키워서 적용하면 이해가능할것이다.

body는 기본적으로 column으로 구성된다.

처음 위젯은 CircleAvatar이다.

해당 위젯또한 Center로 한번 감싸

x축의 센터로 맞춰준다.

백그라운드 이미지는 해당 프로젝트 루트에 저장했기에 AssetImage를 이용해 줬다.

인터넷 url을 통한 방법도 있지만 많을경우 앱이 무거워질수가있다.

radius는 원의 크기이다.

해당 위젯은 Divider로 만들었다.

height는 위아래 여백이며 위 30 아래 30의 공간을 둔다는 의미이다.

color은 hex코드를 이용하지않고 지정된 색을 사용하였다.

thickness는 선의 두깨를 의미한다. 숫자를 변경해보면 이해가능하다.

endIndent는 오른쪽 여백을 의미하며 해당 코드가 없으면 선이 오른쪽에 붙는걸 확인할 수 있을것이다.

4개의 텍스트 위젯을 사용했다.

letterSpacing - 문자간 간격을 의미한다.

fontsize - 글자 크기

fontWeight  - 글자 굵기를 설정할 수 있다.

해당 위젯을보면 가로 2개씩 세로 3개로 구성되어있다.

Row 로 2개(아이콘,텍스트) 묶어 구성하면 된다.

Icon은 Icon(Icons. 을 찍으면 아이콘 종류가 많이 나온다. 원하는걸 쓰면 된다.

SizeBox는 Icon과 Text 사이 여백을 의미한다.

기본적으로 위젯을 디자인하는 방법은 정말 다양하다. 여백을 주는 방법 또한 여러가지이다.

 

얼추 기본 구성은 감이 잡혔지만 계속해서 예제를 통해 더 익혀야겠다.

아래는 풀 코드이다.

 

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'BBANTO',
      home: Grade(),
    );
  }
}

class Grade extends StatelessWidget {
  const Grade({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Color(0xFF69abce),
      appBar: AppBar(
        title: Center(
          child: Text(
            'ID card',
            style: TextStyle(
              fontSize: 30.0,
            ),
          ),
        ),
        backgroundColor: Color(0xff5D8FA9),
        elevation: 0.0,
      ),
      body: Padding(
         padding: EdgeInsets.fromLTRB(30.0, 40.0, 0.0, 0.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Center(
              child: CircleAvatar(
                backgroundImage: AssetImage('assets/2.png'),
                radius: 80.0,
              ),
            ),
            Divider(
              height: 60.0,
              color: Colors.grey[850],
              thickness: 1.5,
              endIndent: 30.0,
            ),
            Text(
              'NAME',
              style: TextStyle(
                color: Colors.white,
                letterSpacing: 2.0,
                fontSize: 20.0,
                fontWeight: FontWeight.bold,
              ),
            ),
            SizedBox(
              height: 10.0,
            ),
            Text(
              'Coding Master',
              style: TextStyle(
                  color: Colors.white,
                  letterSpacing: 2.0,
                  fontSize: 28.0,
                  fontWeight: FontWeight.bold),
            ),
            SizedBox(
              height: 30.0,
            ),
            Text(
              'score',
              style: TextStyle(
                color: Colors.white,
                letterSpacing: 2.0,
                fontSize: 20.0,
                fontWeight: FontWeight.bold,
              ),
            ),
            SizedBox(
              height: 10.0,
            ),
            Text(
              '3.5',
              style: TextStyle(
                  color: Colors.white,
                  letterSpacing: 2.0,
                  fontSize: 28.0,
                  fontWeight: FontWeight.bold),
            ),
            SizedBox(
              height: 30.0,
            ),
            Row(
              children: [
                Icon(
                  Icons.check_circle_outline,
                  size: 25.0,
                ),
                SizedBox(
                  width: 10.0,
                ),
                Text(
                  '1111111111',
                  style: TextStyle(
                    fontSize: 25.0,
                    letterSpacing: 1.0,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ],
            ),
            Row(
              children: [
                Icon(
                  Icons.check_circle_outline,
                  size: 25.0,
                ),
                SizedBox(
                  width: 10.0,
                ),
                Text(
                  '2222222222',
                  style: TextStyle(
                    fontSize: 25.0,
                    letterSpacing: 1.0,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ],
            ),
            Row(
              children: [
                Icon(
                  Icons.check_circle_outline,
                  size: 25.0,
                ),
                SizedBox(
                  width: 10.0,
                ),
                Text(
                  '3333333333',
                  style: TextStyle(
                    fontSize: 25.0,
                    letterSpacing: 1.0,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

플러터를 학습한지 며칠이 지났다.

당장 파이썬 문법만 익힌정도의 수준에서 바로 플러터를 들어가다니 무슨 자신감이였을까...

기본기를 탄탄하게 잡고 플러터를 시작하려고했으나,

시간적인 여유와 현재 공부시간을 살펴볼때 좀 더 현실적인 방법으로 진행하고자

무작정 학습을 시작하며 그때 그때  막히는 부분을 따로 공부하는 프로세스로 진행할 예정이다.

 

현재 플러터는 개발자 시장에서 많은 수요가 있지않다.

하지만 플러터는 충분히 발전가능성이 있고 지금도 가파르게 성장중이다.

크로스 플랫폼 개발을 할 수 있다는것은 정말 큰 장점이라 생각한다.

점점 다양해지는 소스와 활발해지는 커뮤니티를 보았을때 지금도 충분히 매리트가 있다고 생각한다.

 

현재 학습방법은

플러터 홈페이지, 유튜브 강의의 예제를 풀어보며 기본적인 앱 트리구조를 이해하고있다.

유튜브(코딩셰프, 코딩파파, 개발하는남자, 코드팩토리) 

어느정도 감히 잡혔다면 플러터 갤러리에 나와있는 소스코드를 적용해 나만의 앱을 커스터마이징 해보곤한다.

당분간은 프론트에만 집중하려고한다.

 

플러터란..?

플러터는 위젯의 집합체이다.

여러개의 위젯들로 구성된 페이지 앱이다.

그러니 위젯들만 잘 구성할줄안다면

앱 만드는것은 정말 별거 없다.

 

이 위젯들은 2가지 종류로 나뉜다.

어떤 값에 따라 위젯의 데이터가 바뀐다. -> Stateful Widget

어떤 값도 가지고있지 않고 변하지도 않는다. -> Stateless Widget

출처 유튜브 - 코딩셰프

앱을 만들떄 이론상으론 모든 위젯을 Stateful Widget들로만 구성하여 제작해도 되지만,

이러면 앱이 너무 무거워지고 퍼포먼스가 잘 나오지 않는다.

최대한 처음 기획때 잘 구분하여야 한다.

 

유튜브 강의나 책들을 보면 대부분 MaterialApp에 대해서 나와있다.

그러나 플러터 업데이트 이후 CupertinoApp이 나왔는데

쉽게말하면 앱 태마? 정도로 이해하면 된다.

기본적으로는 Material이고 아이폰 감성을 원하는 사용자라면 Cupertino를 이용하면 된다.

맨 윗줄에 import를 통해 사용하면 된다.

Cupertino에 대한 예제들은 플러터 갤러리에 들어가면 다나와있어

쉽게 적용가능할것이다.

 

내가 적용한 예제를 한가지 보자

import 'package:flutter/cupertino.dart';

void main() => runApp(const CupertinoButtonApp());

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

  @override
  Widget build(BuildContext context) {
    return const CupertinoApp( //CupertinoApp을 사용하겠단 선언이다.
      theme: CupertinoThemeData(brightness: Brightness.light),
      home: CupertinoButtonExample(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      navigationBar: const CupertinoNavigationBar(
        middle: Text('CupertinoButton Sample'),
      ),
      child: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            const CupertinoButton(
              onPressed: null,
              child: Text('Disabled'),
            ),
            const SizedBox(height: 30),
            const CupertinoButton.filled(
              onPressed: null,
              child: Text('Disabled'),
            ),
            const SizedBox(height: 30),
            CupertinoButton(
              onPressed: () {},
              child: const Text('Enabled'),
            ),
            const SizedBox(height: 30),
            CupertinoButton.filled(
              onPressed: () {},
              child: const Text('Enabled'),
            ),
          ],
        ),
      ),
    );
  }
}

 

플러터의 기본 구조를 이해하고있다면 위 코드를 쉽게 이해할 수 있을것이다.

+ Recent posts