컴파일러란? 

  -> 사람이 쓰는 고급 프로그래밍언어를 컴퓨터의 저급 프로그래밍 언어(Assembly, 기계어)로 변환 시키는 프로그램

 

컴파일러 종류에는 크게 2가지로 나누어진다

  -> JIT(Just-In-Time)컴파일러 - 런타임에 코드 일부 또는 전부를 컴파일링 하는 기술

       장점 -> 실행결과를 실시간으로 확인 할 수 있다

       단점 -> 상대적으로 느리다, 실행중에 계속 컴파일링 하기 때문에 

 

  -> AOT(Ahead-Of-Time)컴파일러 - 런타임 전에 코드를 컴파일링 하는 기술 

       장점 -> 컴파일이 완료 되어있기 떄문에 실행속도가 빠름

       단점 -> 코드를 고치자 마자 눈으로 확인이 힘들다 

 

Dart언어는 신기하게도 2가지 컴파일러를 모두 사용한다고 한다!

AOT로 성능을 높이고 JIT로 실시간 결과를 확인할 수 있다고 한다

 

또한 Dart는 멀티 플랫폼 개발을 지원한다

(멀티 플랫폼이란 WEB, Mobile, DeskTop환경등을 말한다)

 

Why Not Learning? 

 

개발환경 구축

개발환경을 구축하기 위해 SDK를 설치해야 한다. 

(SDK란? Software Development Kit의 약자로 개발 패키지라고 생각하면 된다)

 

SDK는 총 5가지(컴파일러, 라이브러리, 디버깅툴, 문서, 패키지관리자 등)로 이루어져 있다 

 

SDK를 받을떄 주의할점은 Stable버전을 사용해야 한다!

Beta나 Alpha를 받아 쓰면 안정성이 떨어진다

 

macOS기준 설치하는 방법

  1. Homebrew가 설치되어 있지 않으면 설치한다

  2. brew명령어로 dart를 설치한다 

xcode-select --install
brew tap dart-lang/dart
brew install dart

 

  3. 설치 완료 후 프로젝트 를 생성한다 

dart create [프로젝트이름]

 

  4. 생성된 폴더를 VScode로 열어주면 된다 

 

플러터에는 기본적으로 url을 연결할 수 있는 하이퍼링크 기능이 없다...

 

한참 삽질끝에 방법을 알아내었다 

 

일단 다음과 같이 url_launcher플러그인을 설치한다. 

flutter pub add url_launcher

 

그 다음 https로 연결을 위해 ios에서 권한을 부여해 줘야 한다

권한은 root -> ios -> Runner -> Info.plist에서 

<dict> </dict> 사이에 다음과 같이 추가한다

sms, 전화는 각각 string 안에 sms, tel값을 추가하면 된다 .

<key>LSApplicationQueriesSchemes</key>
    <array>
      <string>https</string>
    </array>

 

 

다음에 버튼이나 이벤트리스너에서 다음과 같이 Uri를 생성하고 url을 launch 해주면 된다 

잊지말고 async를 붙여주도록하자 

final url=Uri.parse('https://google.com');
await launchUrl(url);

 

상태관리로써 StatefulWidget을 사용할 수도 있지만 한계가 존재한다 

 

상태관리가 복잡해 진다거나, 단순화 하려고 하면 성능에 이슈가 생기거나 하는 식이다

 

여러 이슈들을 해결하기 위해 GetX라는 3rd party Library가 만들어 졌다.

기능도 아주 많고 빠르다고 하니 앞으로는 상태관리로 이 친구를 많이 쓸거같다 

 

기본적으로 설치는 프로젝트에서 다음과 같이 진행된다 

flutter pub add get

 

 

이후 상태관리를 하려고 하는 클래스에서 다음과 같이 extends로 상속한다

import 'package:get/get_state_manager/get_state_manager.dart';
import 'package:plmanchu/info/memberInfo.dart';

class Membercontroller extends GetxController {
  List<Memberinfo> members = [
    Memberinfo(
        name: "***",
        MBTI: "****",
        image: 'lib/assets/images/2.png',
        hobbies: ["피아노", "드럼"],
        major: "컴공",
        careers: ["클라우드엔지니어", "k8s개발자"],
        targetApp: "당근마켓",
        introduce: "잘부탁드립니다",
        radarChart: 'lib/assets/images/radarChart.png'),
  ];
}

주의해야 할 점은 member class가 따로 존재해야 한다는 것이다. 

 

이후 main.dart파일에서 의존성을 주입하기 위해 다음과 같이 GetX를 적용한다

* 주의할점 GetX를 사용하면 MaterialApp에서 GetMaterialApp으로 타입을 변경해야 한다

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:plmanchu/info/memberInfo.dart';
import 'package:plmanchu/pages/intro.dart';

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

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    Get.put(Membercontroller())
//  return MaterialApp(
    return GetMaterialApp(

      title: 'PlmanchuIntroducing',
      // theme 은 설정 안해주나요?
      home: IntroPage(),
    );
  }
}

 

 

이후 GetX의 컨트롤러를 사용해서 이벤트를 작동시키기 위해 아래와 같이 

Get.find<controller이름>().(method명)(); 으로 작동시킨다  

아래 코드에서 builder안에, 그리고 Image안에 각각 맴버별로 이미지와 정보를 생성함을 알수 있다. 

children: [
      ...List<Widget>.generate(
          Get.find<Membercontroller>().getMemberLength(), (index) {
        return GestureDetector(
          onTap: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                  builder: (context) => specA(
                        memberInfo: Get.find<Membercontroller>()
                            .getMember(index),
                      )),
            );
          },
          child: Image.asset(
              Get.find<Membercontroller>().getMember(index).image),
        );
      }),
    ],

 

내부에서 동적으로 데이터가 변경되는 경우 다음과 같이 update() 함수를 사용하여 setState() 와 같이 상태를 업데이트 시켜 준다 

update();

 

이렇게 update를 트리거 시켜주면 GetBuilder로 구독한 곳에서 업데이트가 일어나게 된다 

위 소스코드상에는 GetBuilder로 업데이트되는 부분이 현재 없기 때문에 Update시킬 필요가 없다 ㅎ

구독랑이를 하려면 아래와같이 GetBuilder와 controller를 연결시켜주면 쓸수 있다

* 주의할 점은 init과 builder는 반드시 들어가야 하고 builder의 argument는 임의의 컨트롤러 이름이다

child: GetBuilder{
	init: Get.find<컨트롤러 이름>,
	builder: (controller) => {
		Widget...
	}
}

플러터에서 레이아웃을 설정하는 위젯에 대해 알아보자

 

크게 Container, SizedBox, Row, Column, Expanded, Stack&Positioned로 

총 6개정도로 나누어진다고 한다

 

역시 하나하나씩 알아보도록 하자(예제사진을 첨부하지 않겠음)

 

제일먼저 Container

코드를 살펴보면

Container(
  padding: const EdgeInsets.only(
    left: 20,
    right: 20,
  ),
  decoration: BoxDecoration(
    gradient: LinearGradient(
      colors: [
        Color.fromARGB(255, 255, 59, 98).withOpacity(0.7),
        Color.fromARGB(255, 255, 59, 98)
      ],
      begin: Alignment.topLeft,
      end: Alignment.bottomRight,
    ),
    borderRadius: BorderRadius.circular(10),
    boxShadow: [
      BoxShadow(
        color: Color.fromARGB(255, 255, 59, 98).withOpacity(0.5),
        spreadRadius: 5,
        blurRadius: 7,
        offset: Offset(0, 3), // changes position of shadow
      ),
    ],
  ),
  width: 200,
  height: 150,
  child: Center(
      child: Text(
    'Container',
    style: TextStyle(color: Colors.white),
  )),
),

 

이코드 같은 경우 width 200 height 150의 컨테이너가 생성되고 

안에 그라디언트(decoration옵션)로 투명을 준 색깔이 컬러풀하게 빛나는 걸 확인 할 수 있다. 


다음으로 SizedBox

코드로 살펴보자

 

Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
      Container(
        color: Colors.red,
        width: 100,
        height: 40,
      ),
      const SizedBox(height: 10),
      Container(
        color: Colors.blue,
        width: 100,
        height: 40,
      ),
    ],
),

 

특별한게 없다 각 요소 혹은 widget사이에 padding처럼 역할을 수행할 수 있는 SizedBox이다 

height옵션은 높이 즉,위아래 간격으로 나눌때 width옵션은 너비, 즉 좌우로 나눌때 쓰면 된다. 


다음으로 Row 와 Column

코드로 살펴보자 

 

Row(
  mainAxisAlignment: MainAxisAlignment.center,
  children: List.generate(
    5,
    (index) => Container(
      width: 40,
      height: 40,
      color: Colors.red,
      margin: const EdgeInsets.all(5),
    ),
  ),
),

 

Row로 선언 이후에 LIst.generate로 5개 요소를 균등하게 만들어 주고 있다

Row는 말그대로 가로방향으로 요소를 나열하는 LayoutWidget이다 

Column은 Row와 반대로 세로방향으로 요소를 나열하는 LayoutWidget이다 라고 알아두면 된다


다음으로 Expanded

역시 코드로 살펴보자 

Row(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: [
    Expanded(
      child: Container(
        height: 40,
        color: Colors.red,
        margin: const EdgeInsets.all(5),
      ),
    ),
    ...List.generate(
      4,
      (index) => Container(
        width: 40,
        height: 40,
        color: Colors.red,
        margin: const EdgeInsets.all(5),
      ),
    ),
  ],
),

 

Row & Column을 사용할때 많이 합쳐서 사용한다고 한다 

Expanded뜻은 확장하다라는 뜻인데 그냥 Row로 선언한 요소의 경우 딱 그 필요한 width만 차지하는 반면

Expanded는 가로영역 전체를 다 차지하게 된다. 

 

CSS에서 존재하는 Flex옵션도 있는데 비율로 자리를 차지하는 옵션이다.

코드는 다음과 같다 

Row(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: [
    Expanded(
      flex: 1, // 1
      child: Container(
        height: 40,
        color: Colors.red,
        margin: const EdgeInsets.all(5),
      ),
    ),
    Expanded(
      flex: 3, // 2
      child: Container(
        height: 40,
        color: Colors.red,
        margin: const EdgeInsets.all(5),
      ),
    ),
    Expanded(
      flex: 2, // 1
      child: Container(
        height: 40,
        color: Colors.red,
        margin: const EdgeInsets.all(5),
      ),
    ),
  ],
),

마지막으로 Stack&Positioned

사진으로 간단하게 살펴보면 아래 사진과 같이 프로필 밑에 사진버튼같은걸 더할때 Stack을 사용하게 된다. 

코드로 살펴보자 

Stack(
  children: [
    const CircleAvatar(
      radius: 50,
      child: Icon(
        Icons.person,
        size: 40,
      ),
    ),
    Positioned(
      bottom: 0,
      right: 0,
      child: Container(
        padding: const EdgeInsets.all(7),
        decoration: const BoxDecoration(
            shape: BoxShape.circle, color: Colors.white),
        child: const Icon(
          Icons.camera_enhance,
          size: 24,
        ),
      ),
    )
  ],
),

 

Stack을 선언하고 아래 children으로 CircleAvatar과 그 위에 띄워지는 Positioned로 선언된 카메라 아이콘이 있다 

Bottom, Right 옵션을 주면 우하단에 Stack이 위치하게 되며 

Top, Left 옵션을 주게되면 좌상단에 Stack이 위치한다

 

 

 

이렇게 LayoutWidget의 종류를 알아보았다. 

 

PageView와 함께 사용하면 여러가지 레이아웃을 만들수 있을거같다. 

 

 

플러터에는 View Widget으로 크게 4종류가 있다 

PageView, ListView, GridView, TapbarView, 

 

하나하나씩 살펴보자 

 

PageView는 아래 사진과 같이 슬라이드 형식으로 애니매이션이 들어가 있는 뷰이다 

 

코드로  예제를 살펴보면 다음과 같이 나타낼수 있다. 

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: PageView(
        children: [
          Container(
            color: Colors.red,
            child: const Center(
              child: Text(
                "1",
                style: TextStyle(fontSize: 50, color: Colors.white),
              ),
            ),
          ),
          Container(
            color: Colors.blue,
            child: const Center(
              child: Text(
                "2",
                style: TextStyle(fontSize: 50, color: Colors.white),
              ),
            ),
          ),
          Container(
            color: Colors.yellow,
            child: const Center(
              child: Text(
                "3",
                style: TextStyle(fontSize: 50, color: Colors.white),
              ),
            ),
          ),
        ],
      )),
    );
  }
}

Body안에 PageView를 생성하고 children으로 요소를 설정해 주면 저 위에 사진처럼 동작하는 View를 만들 수 있다, 

다양한 옵션은 덤, 여러 유용한 옵션이 있으니 구글에 찾아볼 수 있도록 하자 


 

ListView같은 경우에는  아래 사진과 같이 말그대로 여러 리스트를 한페이지에서 보여주는 View라고 할 수 있다

샘플코드는 다음과 같다 

Body에서 ListViewf를 설정해주고 똑같이 children으로 요소를 받아서 프린트 하는 식이다

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: ListView(
          scrollDirection: Axis.horizontal,
          children: List.generate(
            10,
            (index) => Container(
              width: 100,
              height: 100,
              margin: const EdgeInsets.all(5),
              color: Colors.red.withAlpha((index + 1) * 25),
            ),
          ),
        ),
      ),
    );
  }
}

 

유용한 옵션중 하나인 reverse는 true로 주게 되면 제일 첫번째 요소가 axis의 제일 마지막에 위치하게 된다 

또한 Physics옵션도 있는데 컨트롤러를 연결하고 사용하면 된다고 한다 


다음으로는 GridView 이다 

GridView는 다음과 같이 N * N 의 요소를 표현할때 사용하면 좋다 

코드로는 아래와 같이 역시 GridView로 정의 하고 사용하는 모습을 볼수 있다. 

주의 해야할 점은 gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount 옵션을 필수로 들어가야 하고 

crossAxisCount가 그 축에서 나타낼 요소의 수를 나타낸다고 보면 된다. 나머지 옵션은 spacing, 즉 얼마나 띄어서 요소를 나타낼건지

결정한다고 보면 된다. 

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return  MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: GridView(
          gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 3,
            mainAxisSpacing: 2,
            crossAxisSpacing: 2,
          ),
          children: List.generate(
            100,
            (index) => Center(
              child: Container(
                color: Colors.grey,
                child: Center(child: Text(index.toString())),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

 

또한 다른 옵션도 있는데 아래와 같ㅌ이 옵션을 주게 되면

각 요소가 최대 얼마나 길이를 가지고 있을수 있는지 정해주는 옵션이다. 

          gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
            maxCrossAxisExtent: 300,
            mainAxisSpacing: 2,
            crossAxisSpacing: 2,
          ),

마지막 요소인 TabBarView 이다 

말그대로 상단 혹은 하단에 위치하면서 다른 탭을 나타낼때 사용하면 된다. 

 

 

TabBarView는 다른 View와는 다르게 2가지 요소로 구성이 되는데 

말 그대로 Tab을 나타내는 요소와 Body를 나타내는 요소 2가지로 나누어지게 된다. 

 

코드를 살펴보자 

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

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(body: const SampleWidget()),
    );
  }
}

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

  @override
  State<SampleWidget> createState() => _SampleWidgetState();
}

class _SampleWidgetState extends State<SampleWidget>
    with TickerProviderStateMixin {
  late TabController _tabController;

  @override
  void initState() {
    super.initState();
    _tabController = TabController(
      length: 3,
      vsync: this,
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          TabBar(
            controller: _tabController,
            labelColor: Colors.blue,
            unselectedLabelColor: Colors.grey,
            labelPadding: const EdgeInsets.symmetric(vertical: 20),
            tabs: const [
              Text('메뉴1'),
              Text('메뉴2'),
              Text('메뉴3'),
            ],
          ),
          Expanded(
            child: TabBarView(
              controller: _tabController,
              children: [
                Container(
                  color: Colors.blue,
                  child: Center(child: Text('메뉴1 페이지 ')),
                ),
                Container(
                  color: Colors.blue,
                  child: Center(child: Text('메뉴2 페이지 ')),
                ),
                Container(
                  color: Colors.blue,
                  child: Center(child: Text('메뉴3 페이지 ')),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

 

양이 좀 많지만 어렵지 않다. 

 

SampleWidget은 StatefulWidget을 상속받아 State를 유지하게 된다. 

그리고 컨트롤러가 중요하다! TabController를 late로 선언하고 initState()에서 초기화 해야 한다 

vsync는 this를 사용하기 위해 with TickerProviderStateMixin을 같이 선언해준다

(with ~는 animation을 사용할 경우 항상 사용해야 한다고 한다[싱크맞추기위해])

 

Tabbar의 메뉴개수와 아래 body에서 페이지 개수는 같아야한다, 다르면 오류가 난다 

 

Body는 Contoller를 반드시 넣어줘야 한다는걸 잊지말자

페이지를 나타낼때는 Expanded로 선ㅇ넌하고 아래 컨트롤러를 정하고 내용을 넣어주면 된다

 

Tab은 아래와 같이 옵션을 정할수 있다

TabBar(
  controller: _tabController,
  labelColor: Colors.blue,
  unselectedLabelColor: Colors.grey,
  labelPadding: const EdgeInsets.symmetric(vertical: 20),
  tabs: const [
    Text('메뉴1'),
    Text('메뉴2'),
    Text('메뉴3'),
  ],
),

 

 

 

팀 스파르타에서 운영하는 내일배움캠프중 하나인 플러터 앱창업 5기를 등록하고 241021부로 수업이 시작되었다

 

TIL과 WIL를 위해 기록을 남겨야 하기 때문에 오랜만에 글을 작성하게 되었다..

 

글쓰는게 어색하고 적응이 안된다. 

 

배운건 많은데 그걸 다 적기엔 시간이 오래 걸릴거 같기도 하고

 

일단 필요한것만 적어보자 

+ Recent posts