flutter create를 통해 프로젝트를 생성하면 아래 사진과 같은 구조로 파일구조가 생성되게 된다. 

파일구조를 살펴보자 

 

제일먼저 lib 폴더는 프로그램에서 사용되는 dart파일을 넣어두는 폴더이다.(사진에서도 main.dart가 포함되어있다)
실제 여기서 여러 폴더구조를 만들고 파일을 생성하여 코딩을 하는 폴더이다 

 

다음 android, ios, linux, macos, web폴더인데 각각 디바이스에서 사용할 수 있는 센서등을 사용할때
native언어로 코드를 작성해서 사용하면 된다고 한다 

 

test폴더는 TDD를 위해 lib폴더의 테스트를 생성하기 위한 폴더이다 

 

다음으로 pubspec.yaml은 프로젝트 구성에 대해 선언이 있는곳인데, SDK버전이나, 사용할 라이브러리등을 선언한다
사용방법은 pub.dev에서 선언되있는 dependency를 긁어서 복붙하거나 flutter pub add명령어를 사용해서 라이브러리를 추가하는 방식이다 

 

그 외 파일들은 딱히 프로젝트 사용에 있어 건드릴 필요가 없어 소개는 여기까지 하기로 한다. 

728x90

비동기 프로그래밍이란?

 

일단 동기(Syncronous)는 무엇이고 비동기(Asyncronous)는 무엇인가?

  -> 동기는 blocking되는 코드 -> 이전 명령이 안끝나면 다음명령이 실행 되지 않음

  -> 비동기는 blocking되지 않는 코드 -> 이전 명령이 안끝나도 다음명이 실행 됨 

 

왜 동기적인 작업이 문제가 될까? 예를들어

  -> 파일을 불러올때

  -> 데이터베이스에 데이터를 쓸때

  -> 네트워크로부터 데이터를 불러올때

등등 여러 케이스에서 한 작업이 끝날때까지 뒤의 모든 작업이 기다리게 된다. 

이럴 경우 시간적으로 손해가 크기때문에 동기화된 작업은 추천 되지 않는다 

 

그렇다면 비동기는? 작업이 완료될때까지 기다리지 않고 다음 작업을 바로 실행함(병렬적)

그렇다면 결과값은? 작업이 마무리되면 그때 데이터를 받음 

 

dart에서는 dart.async 라이브러리를통해 Future, Stream기능, 비동기 프로그래밍을 지원하게 된다 

 

Future을 알아보자

Future을 사용할 때 반환 되는 타입은 Future타입이다. Future타입안에 데이터타입이 명시 된다. 

Future<int> number = Future.value(1);
Future<String> name = Future.value('스트링');
Future<bool> isTrue = Future.value(true);

 

언제 쓰일까?

  -> 하나의 작업에 대해 이벤트가 한번 발생하는 단일 비동기 작업에 사용된다. 

void main(){
  int seconds = 2;
  print('start');
  Future.delayed(Duration(seconds: seconds), () {
    print("waited $seconds");
  });
  print('done');
}

// 출력되는 결과
/*
  start
  done
  waited 2
*/

 

비동기 작업을 동기화 처럼 사용하고자 할때(순서를 지켜서 실행해야할때) async, await키워드를 사용해서 코드를작성한다 

void main() async{
  int seconds = 2;
  print('start');
  await Future.delayed(Duration(seconds: seconds), () {
    print("waited $seconds");
  });
  print('done');
}

// 출력되는 결과
/*
  start
  waited 2
  done
*/

 

하지만 Future은 단 한번만 결과값을 받게 된다. 그래서 Stream을 사용한다

 

Stream에 대해 알아보자

제일 큰 특성은 시간에 따라 연속적인 데이터 흐름을 제공한다

연속적으로 데이터를 받을 수 있기 때문에 프로그램이 임의로 Stream을 종료하지 않는다

사용자가 Stream을 시작하고 끝을 내야한다.

 

예제로 알아보기전에 yield와 listen() method를 알아야 한다 

 

yield는 값을 방출하도록 하는 키워드 -> return 과 비슷하다고 생각하면 된다

listen()은 yield로 방출되는 값을 얻기위해 사용되는 메서드다

 

예제로 알아보자 

참고로 Stream을 적용한 함수에는 async* 키워드를 달아주어야한다 async가 아니다 

아래 예제는 입력받은 숫자부터 0까지 1초를 기다리면서 출력한는 예제이다 

Stream<int> emitNUmbers(int first) async* {
  for(var i = first; i >= 0; i--){
    yield i;
    
    await Future.delayed(Duration(seconds:1));
  }
}

void main() {
  emitNumbers(10).listen((number) {
    print(number);
  }
}

/*
  10
  9
  8
  7
  6
  5
  4
  3
  2
  1
  0
  이 차례대로 출려 됨 
*/

 

참고로 고차함수로 체이닝하여 사용할 수도 있다고 한다. 

체이닝을 사용하면 특정 값만 걸러내서 받을 수도 있을거같다. 

728x90

함수형 프로그래밍에 대해서 배워보자

 

첫번째 정의는 함수의 연속으로 프로그램을 구성하는 것이라고 생각하면 된다고 한다 

Method Chaining이라고도 한다고 한다 

  - ' . ' 을사용해서 여러개의 함수를 하나로 연결하는 방식

 아래 코드와 같이 여러 method 를 연속적으로 사용하는 방식

var result = number.abs().toString();

 

두번째 정의는 가변적인 데이터의 사용을 최소화하여 프로그램을 구성하는 방식이라고 하는데 

예제로 살펴보자 

아래와 같이 매개변수에만 의존하는 함수의 형태를 Pure FUnction(순수 함수)라고 한다 

int add(int a, int b){
  return a + b;
}

또다른 예제로는 아래와 같은 함수도 있다

잘 보면 매개변수를 의존하여 값을 계산하기 때문이다(스코프 안에서만 변경한다)

int getTotal(List<int> numbers) {
  int result = 0;
  for (var number in numbers){
   result += numbers;
  }
  return result;
}

 

함수형 프로그래밍에서 많이 사용하는 함수에는 2가지가 있다

  - 형변환 함수(Type Casting Function)

    -> 특정 데이터를 다른 타입의 데이터로 변경하는 함수(toString(), int.parse('')같은 함수)

  - 고차 함수(Higher-order function)

    -> map(), where(), reduce(), fold(), any(), every(), takeWhile()등  여러가지 종류가 있으며 중요한 몇가지 함수를 알아보자

   

      -> map()은  Collection타입의 데이터 각요소에 특정 함수를 적용한 새로운 collection을 반환하는 함수이다(shallow copy)

      -> 예제는 아래 코드와 같다. List의 모든 요소에 특정 작업을 해서 새로운 List를 반환하고 있다. 

//map(([매개변수]) { return [동작] });
List<String> fruitList = ['a', 'b', 'c'];
var delicious =. ruitList.map((fruit) {
  var word = '맛있는';
  word += fruit;
  reutrn word;
});
print(delicious); // 맛있는 a, 맛있는 b, 맛있는 c 출력

//map(([매개변수]) => [동작]); 
List<String> fruitList = ['a', 'b', 'c'];
var delicious = fruitList.map((fruit) => '맛있는 + $fruit');
print(delicious); // 맛있는 a, 맛있는 b, 맛있는 c 출력

     

      -> where()은 Collection 요소중 참인 조건에 부합되는 요소만 필터링한 컬렉션을 반환한다 

      -> 예제는 아래와 같다. Return안에 bool값을 판별할 수 있는 식이 존재해야 한다

      -> 자매품으로 firstWhere(), lastWhere()등이 있다만 얘네들은 참인 요소가 없을때는 에러를 뱉는다(stateError)

// where(([매개변수]) { return [조건식]; }
List<int> numbers = [1,2,3,4,5,6,7];
var result = numbers.where((number) {
  return number > 5;
});
print(result); // 6,7 출력

 

      -> reduce()는 collection에 있는 요소들을 하나의 값으로 결합하는 함수

      -> 예제는 아래와 같다, 리스트를 순회하면서 앞에 값을 뒤에 값에 계속더하면서 진행된다

      -> 특징은 collection의 데이터타입 과 같은 타입으로만 반환할 수 있고 데이터가 비어있을경우 에러가 발생한다(stateError)

// reduce(([매개변수1], [매개변수2]) { return [동작] };
List<int> numbers = [1,2,3,4,5];
var result = numbers.reduce((a, b) {
  return a + b
});
print(result); // 15 출력

 

      -> fold()는 reduce에서 초기값이 추가된다. reduce()와는 다르게 초기값이 초기값으로 세팅된다. 

      -> 특이사항으로는 다른 타입으로도 반환이 가능하고 요소가 없어도 에러가 발생 안한다고 한다. 

// fold(초기값, ([매개변수1], [매개변수2]) { return [동작] });
// 앞에 초기값이 추가된다

List<int> numbers = [1,2,3,4,5];
var result = numbers.fold(0, (a, b) {
  return a + b
});
print(result); // 15 출력

 

 

      -> any()는 주어진 collection중 하나라도 조건을 만족시 true를 반환한다(where과 비슷하지만 bool을 반환한다)

      -> every()는 collection에서 모든 요소가 맞아야 true가 반환된다 

      -> takeWhile()은 collection에서 true인 요소가 나오는 동안에는 요소를 반환하고 false로 나오는 요소부터는 무시하는 기능이다

      -> skipWhile()은 collection에서 true인 요소가 나오는 동안에는 요소를 스킵하고 false로 나오는 순간부터의 요소를 모두 반환한다

 

Method Chaining은 위 고차함수를 잘 조합해서 쓰는게 핵심이다 

map() + where(),

where() + reduce(),

where() + map() + fold() 등등

Chaining을 통해 collection에서 필요한 여러 작업을 짧은 코드수로 끝낼 수 있다

728x90

플러터에는 기본적으로 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);

 

728x90

상태관리로써 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...
	}
}
728x90

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

 

크게 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와 함께 사용하면 여러가지 레이아웃을 만들수 있을거같다. 

 

 

728x90

플러터에는 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'),
  ],
),

 

 

 

728x90

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

 

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

 

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

 

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

 

일단 필요한것만 적어보자 

728x90

+ Recent posts