반응형

Flutter 레이아웃의 기본, Container 위젯을 이해하자

 

Flutter에서 Container 위젯은 레이아웃을 구성하는 데 가장 기본적이면서도 강력한 도구입니다. 이를 비유하자면, 빈 상자(Box) 또는 캔버스처럼 생각할 수 있습니다. 이 빈 캔버스 위에 원하는 스타일, 정렬, 크기 등을 적용해 다양한 레이아웃을 구성할 수 있습니다.


1. Container란 무엇인가?

 

Container 위젯은 Flutter에서 레이아웃을 꾸미고 배치하는 데 사용되는 다목적 위젯입니다. 주요 기능은 다음과 같습니다:

크기 조절: width, height를 통해 크기를 조정할 수 있습니다.

스타일링: color, decoration을 사용해 배경색, 테두리, 그림자 등을 추가할 수 있습니다.

배치: 내부 여백(padding), 외부 여백(margin), 정렬(alignment) 등을 설정할 수 있습니다.

 

2. Container의 주요 속성

 

Container는 다양한 속성을 제공하며, 이를 통해 위젯을 쉽게 커스터마이징할 수 있습니다.

속성 설명
width / height 컨테이너의 너비와 높이를 설정합니다.
color 컨테이너의 배경색을 지정합니다.
padding 내부 여백을 설정하여 컨테이너 내부의 자식 위젯이 테두리로부터 얼마나 떨어져야 하는지 결정합니다.
margin 외부 여백을 설정하여 컨테이너와 주변 위젯 간의 간격을 조정합니다.
decoration 배경, 테두리, 그림자, 둥근 모서리 등의 복잡한 스타일을 설정할 수 있습니다. color와 함께 사용하지 못하며, 둘 중 하나만 사용 가능합니다.
alignment 자식 위젯의 배치를 컨테이너 내부에서 조정합니다.
constraints 컨테이너의 최소 및 최대 크기를 설정합니다.
child Container 내부에 들어갈 다른 위젯 설정(모든 위젯에 자식 위젯을 지정할 수 있음)

 


3. 실제 예제 코드로 이해하기

 

기본적인 Container

Container(
  width: 100,
  height: 100,
  color: Colors.blue, // 단순 배경색 설정
)

여백과 정렬 추가

Container(
  margin: EdgeInsets.all(20), // 외부 여백
  padding: EdgeInsets.all(10), // 내부 여백
  color: Colors.orange,
  child: Text('Hello Flutter!'), // 자식 위젯
)

스타일링과 둥근 테두리

Container(
  width: 150,
  height: 150,
  decoration: BoxDecoration(
    color: Colors.green,
    borderRadius: BorderRadius.circular(15), // 둥근 모서리
    boxShadow: [
      BoxShadow(
        color: Colors.black.withOpacity(0.2),
        spreadRadius: 2,
        blurRadius: 5,
      ),
    ],
  ),
  child: Center(
    child: Text(
      'Styled Box',
      style: TextStyle(color: Colors.white),
    ),
  ),
)

 

 

4. Container의 한계

 

Container를 지나치게 남용하면 성능에 영향을 미칠 수 있습니다. Flutter는 레이아웃을 효율적으로 구성하기 위해 더 간단한 위젯(예: Padding, SizedBox, Align)을 사용할 수 있습니다. 따라서 Container를 사용할 때 꼭 필요한 경우에만 활용하는 것이 좋습니다.

반응형
반응형

Flutter의 didChangeDependencies 함수란?

Flutter에서 didChangeDependencies 함수StatefulWidget의 상태가 변경되었거나, 위젯이 의존하고 있는 어떤 값이 변경되었을 때 호출되는 함수입니다.
쉽게 말해, "위젯이 외부에서 제공받는 데이터가 변경되었을 때 실행되는 함수"입니다.

 


didChangeDependencies의 주요 특징

  1. 특정 상황에서 호출:
    • 위젯의 의존성이 변경되었을 때 호출됩니다.
    • 예: InheritedWidget의 값이 변경되었을 때.
  2. initState 다음에 호출:
    • 위젯이 처음 생성될 때, initState 이후에 한 번 호출됩니다.
    • 그 이후에는 의존성이 변경될 때마다 호출됩니다.
  3. 데이터 의존성이 있을 때 사용:
    • 위젯이 상위 위젯에서 제공하는 데이터를 사용할 때, 이 데이터를 다시 불러오거나 처리하는 데 유용합니다.

didChangeDependencies 함수의 기본 구조

@override
void didChangeDependencies() {
  super.didChangeDependencies();
  // 의존성 변경 시 수행할 작업
}
  • super.didChangeDependencies(): Flutter의 기본 동작을 유지하려면 항상 호출해야 합니다.

간단한 예제: InheritedWidget과 함께 사용

아래는 InheritedWidget을 사용해 데이터를 전달하고, didChangeDependencies에서 이 데이터를 처리하는 예제입니다.

import 'package:flutter/material.dart';

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

// InheritedWidget 정의
class CounterProvider extends InheritedWidget {
  final int counter;

  CounterProvider({
    required this.counter,
    required Widget child,
  }) : super(child: child);

  @override
  bool updateShouldNotify(CounterProvider oldWidget) {
    return oldWidget.counter != counter;
  }

  static CounterProvider? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<CounterProvider>();
  }
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  int counter = 0;

  void _incrementCounter() {
    setState(() {
      counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return CounterProvider(
      counter: counter,
      child: MaterialApp(
        home: Scaffold(
          appBar: AppBar(title: Text('didChangeDependencies 예제')),
          body: CounterDisplay(),
          floatingActionButton: FloatingActionButton(
            onPressed: _incrementCounter,
            child: Icon(Icons.add),
          ),
        ),
      ),
    );
  }
}

class CounterDisplay extends StatefulWidget {
  @override
  _CounterDisplayState createState() => _CounterDisplayState();
}

class _CounterDisplayState extends State<CounterDisplay> {
  int? counter;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // InheritedWidget에서 데이터를 가져옴
    counter = CounterProvider.of(context)?.counter;
    print('didChangeDependencies 호출됨: counter = $counter');
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(
        'Counter: $counter',
        style: TextStyle(fontSize: 24),
      ),
    );
  }
}

실행 결과:

  1. 처음 실행하면 Counter: 0이 표시됩니다.
  2. 플러스 버튼을 누르면:
    • counter 값이 증가하고, didChangeDependencies가 호출되어 화면이 업데이트됩니다.
    • 콘솔에 didChangeDependencies 호출됨이 출력됩니다.

언제 didChangeDependencies를 사용해야 할까?

  • InheritedWidget 데이터 의존:
    • 위젯이 상위 위젯에서 데이터를 받을 때, 데이터 변경을 감지하고 싶을 때.
  • 외부 의존성 감지:
    • 예: 테마 변경, 지역화(Localization) 데이터 변경.

didChangeDependencies와 initState의 차이

initState didChangeDependencies
위젯이 처음 생성될 때 한 번 호출됨. 처음 생성될 때 한 번 호출되고, 이후 의존성이 변경될 때마다 호출됨.
내부 상태를 초기화하는 데 사용됨. 외부 데이터(의존성) 변경을 감지하고 처리함.
context를 사용할 수 없음 context를 사용할 수 있음.
반응형
반응형

Flutter의 initState 함수란?

Flutter에서 initState 함수StatefulWidget의 상태를 초기화할 때 사용하는 메서드입니다.
쉽게 말해, "앱이 처음 실행될 때 한 번만 호출되는 준비 작업 함수"라고 생각하면 됩니다.
예를 들어, 데이터를 불러오거나 타이머를 설정하는 등 초기화가 필요한 작업을 initState에서 수행합니다.


initState의 주요 특징

  1. 딱 한 번 호출됨
    • initState는 State 객체가 처음 생성될 때 한 번만 호출됩니다.
    • 이후 상태가 변경되어도 initState는 다시 호출되지 않습니다.
  2. 초기화 작업에 적합
    • 데이터를 불러오거나 리스너를 추가하는 등 초기 설정 작업에 사용됩니다.
  3. super.initState() 호출 필요
    • initState를 오버라이드할 때 super.initState() 를 반드시 호출해야 Flutter의 내부 초기화가 제대로 동작합니다.

initState 함수의 기본 구조

@override
void initState() {
  super.initState(); // 부모 클래스의 초기화 메서드 호출
  // 초기화 작업
}

간단한 예제: 앱 시작 시 타이머 설정

아래는 앱이 실행되면 3초 후에 텍스트를 업데이트하는 예제입니다.

import 'package:flutter/material.dart';
import 'dart:async'; // 타이머를 사용하기 위해 필요

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String message = "안녕하세요!";

  @override
  void initState() {
    super.initState();
    // 3초 후에 메시지 변경
    Timer(Duration(seconds: 3), () {
      setState(() {
        message = "Flutter를 배우고 있어요!";
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('initState 함수 예제')),
        body: Center(
          child: Text(
            message,
            style: TextStyle(fontSize: 24),
          ),
        ),
      ),
    );
  }
}

 


언제 initState를 사용해야 할까?

  • 초기 데이터 로드:
    • 예: API 호출, 데이터베이스에서 데이터 가져오기.
  • 애니메이션 시작:
    • 애니메이션 컨트롤러 초기화.
  • 리스너 추가:
    • 텍스트 입력이나 네트워크 이벤트를 감지하는 리스너 설정.
  • 타이머 설정:
    • 특정 시간 후에 동작을 실행.

 

주의할 점

  1. UI 관련 작업 금지:
    • initState에서 직접 UI를 업데이트하거나 context를 사용해 위젯 트리에 접근하면 오류가 발생할 수 있습니다.
    • 이유: 위젯이 아직 완전히 렌더링되지 않았기 때문입니다.
  2. 반드시 super.initState() 호출:
    • initState를 오버라이드할 때 Flutter의 기본 초기화 작업을 위해 super.initState()를 호출해야 합니다.
  3. dispose와 함께 사용:
    • 리스너나 애니메이션 컨트롤러를 initState에서 추가했다면, dispose 함수에서 반드시 정리(cleanup)해야 메모리 누수를 방지할 수 있습니다.
반응형
반응형

Flutter의 createState 함수란?

Flutter에서 createState 함수StatefulWidget과 함께 사용되며, 화면의 변경 가능한 상태(state)를 관리하는 중요한 역할을 합니다. 쉽게 말해, StatefulWidget을 위한 "상태 관리자"를 만들어주는 함수입니다.


왜 createState가 필요한가?

StatefulWidget은 두 가지 주요 구성 요소로 이루어져 있습니다:

  1. StatefulWidget: 화면에 정적인 부분을 정의합니다.
  2. State: 화면의 변경 가능한 부분(예: 버튼 클릭, 입력 값)을 관리합니다.

createState 함수는 StatefulWidget이 화면의 상태를 관리할 수 있도록 State 객체를 연결합니다.

 

createState 함수의 기본 구조

createState 함수는 StatefulWidget 클래스에서 필수로 구현해야 하며, State 클래스를 반환합니다.

@override
State<MyWidget> createState() => _MyWidgetState();

 

  • MyWidget: StatefulWidget 클래스 이름입니다.
  • _MyWidgetState: State 클래스 이름입니다. 일반적으로 밑줄(_)을 붙여 해당 클래스를 비공개로 만듭니다.

간단한 예제: 버튼을 누르면 숫자가 증가하는 앱

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  int counter = 0;

  void _incrementCounter() {
    setState(() {
      counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('createState 함수 예제'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text(
                '버튼을 누른 횟수:',
                style: TextStyle(fontSize: 20),
              ),
              Text(
                '$counter',
                style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold),
              ),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: _incrementCounter,
                child: Text('숫자 증가'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

 

 

 


createState 함수의 동작 과정

  1. State 객체 생성:
    • createState 함수는 State 객체를 생성하고 StatefulWidget과 연결합니다.
  2. State 객체 관리:
    • Flutter는 State 객체를 사용해 StatefulWidget의 상태를 관리합니다.
  3. 상태 변경:
    • 상태가 변경되면 setState 메서드를 호출하여 build 함수를 다시 실행하고 UI를 업데이트합니다.

StatefulWidget에서 createState와 State의 관계

StatefulWidget은 변경 가능한 UI를 처리할 때 항상 State 객체와 함께 작동합니다. 아래 그림으로 쉽게 이해할 수 있습니다:

StatefulWidget -------> createState() -------> State 클래스
       ↑                                          ↓
      UI 정의                              상태 변경 및 UI 업데이트

1. createState 함수는 언제 호출되나요?

  • StatefulWidget이 처음 생성될 때 딱 한 번 호출됩니다.

2. 왜 State 객체를 분리하나요?

  • State 객체를 분리하면 동일한 StatefulWidget을 다른 상태에서 재사용할 수 있어 UI와 상태를 독립적으로 관리할 수 있습니다.

3. State 클래스의 생명주기는 어떻게 되나요?

State 클래스는 아래와 같은 순서로 작동합니다:

  1. createState: State 객체 생성.
  2. initState: State 객체 초기화.
  3. build: UI 렌더링.
  4. setState: 상태 변경 후 UI 업데이트.
반응형
반응형

Flutter의 build 함수란?

Flutter에서 UI를 만드는 핵심 메서드 중 하나가 바로 build 함수입니다. 앱 화면에 표시되는 모든 것은 이 build 함수에서 정의됩니다. 쉽게 말해, build 함수는 화면에 어떤 위젯을 보여줄지 "그리는 설계도"라고 할 수 있습니다.

 


build 함수의 역할

  1. 위젯 트리를 구성:
    • build 함수는 Flutter에게 어떤 위젯을 화면에 보여줄지 알려줍니다.
    • 이 함수 안에서 다양한 위젯을 조합해 "위젯 트리(widget tree)"를 만듭니다.
  2. UI 업데이트 처리:
    • 앱의 상태(state)가 바뀌면, Flutter는 build 함수를 다시 호출해 변경된 UI를 업데이트합니다.

build 함수의 기본 구조

build 함수는 반드시 구현해야 하는 함수로, 모든 Flutter 위젯에서 사용됩니다. 아래는 build 함수의 기본 구조입니다.

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

 

  • context: 현재 위젯의 위치와 관련된 정보를 담고 있습니다.
  • 반환 값: 다른 위젯(또는 위젯 트리)을 반환합니다.

간단한 예제: build 함수에서 텍스트 표시

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Build 함수 예제')),
        body: Center(
          child: Text('안녕하세요! Flutter를 배우고 있어요!'),
        ),
      ),
    );
  }
}

 


StatefulWidget과 build 함수

StatefulWidget에서는 상태가 변경될 때마다 build 함수가 호출되어 UI를 다시 그립니다.

상태에 따라 버튼을 누르면 카운터가 증가하는 예제

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  int counter = 0;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('카운터 앱')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text('버튼을 누른 횟수: $counter', style: TextStyle(fontSize: 24)),
              ElevatedButton(
                onPressed: () {
                  setState(() {
                    counter++;
                  });
                },
                child: Text('숫자 증가'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}


build 함수의 중요한 특징

  1. 반복 호출:
    • build 함수는 앱 상태가 변경될 때마다 반복적으로 호출됩니다.
    • 하지만 Flutter는 효율적으로 변경된 부분만 업데이트하므로 걱정하지 않아도 됩니다.
  2. 항상 위젯 반환:
    • build 함수는 항상 위젯 또는 위젯 트리를 반환해야 합니다.
  3. UI 선언:
    • build 함수는 "어떤 UI를 보여줄지"만 선언하며, 렌더링이나 UI 업데이트는 Flutter가 자동으로 처리합니다.
반응형
반응형

Dart의 Constructor(생성자)

생성자(Constructor)는 클래스의 인스턴스를 생성하고 초기화하는 데 사용되는 특별한 메서드입니다. Dart에서 생성자는 클래스와 동일한 이름을 가지며, 객체를 생성할 때 호출됩니다. 생성자는 객체의 초기 상태를 설정하거나, 특정 작업을 수행하면서 객체를 초기화할 때 주로 사용됩니다.


생성자의 주요 특징

  1. 클래스와 동일한 이름을 가집니다.
  2. 객체가 생성될 때 자동으로 호출됩니다.
  3. 반환 타입이 없습니다.
  4. 기본 생성자가 없을 경우, Dart는 자동으로 기본 생성자(매개변수 없는 생성자)를 제공합니다.

생성자의 종류

1. 기본 생성자

Dart에서 생성자는 클래스와 동일한 이름을 가지며, 매개변수를 통해 필드 값을 초기화할 수 있습니다.

예시:

class Person {
  String name;
  int age;

  // 기본 생성자
  Person(String name, int age) {
    this.name = name; // this를 사용하여 클래스 필드를 초기화
    this.age = age;
  }

  void introduce() {
    print('안녕하세요! 저는 $name이고, 나이는 $age입니다.');
  }
}

void main() {
  Person person = Person('홍길동', 25);
  person.introduce();
}

출력:

안녕하세요! 저는 홍길동이고, 나이는 25입니다.

2. 간략화된 생성자 (축약 생성자)

Dart에서는 축약 문법을 사용하여 필드를 간단하게 초기화할 수 있습니다. this 키워드를 사용하여 클래스의 필드를 직접 초기화합니다.

예시:

class Person {
  String name;
  int age;

  // 축약 생성자
  Person(this.name, this.age);

  void introduce() {
    print('안녕하세요! 저는 $name이고, 나이는 $age입니다.');
  }
}

void main() {
  Person person = Person('홍길동', 25);
  person.introduce();
}

출력:

안녕하세요! 저는 홍길동이고, 나이는 25입니다.

3. 이름이 지정된 생성자 (Named Constructor)

여러 생성자를 사용할 경우, 이름을 지정하여 다양한 방식으로 객체를 초기화할 수 있습니다.

예시:

class Person {
  String name;
  int age;

  // 기본 생성자
  Person(this.name, this.age);

  // 이름이 지정된 생성자
  Person.namedConstructor(String name) {
    this.name = name;
    this.age = 0; // 나이는 기본값으로 설정
  }

  void introduce() {
    print('안녕하세요! 저는 $name이고, 나이는 $age입니다.');
  }
}

void main() {
  Person person1 = Person('홍길동', 25);
  person1.introduce();

  Person person2 = Person.namedConstructor('이순신');
  person2.introduce();
}

출력:

안녕하세요! 저는 홍길동이고, 나이는 25입니다.
안녕하세요! 저는 이순신이고, 나이는 0입니다.

4. 초기화 리스트 (Initializer List)

초기화 리스트는 생성자가 본문을 실행하기 전에 필드 값을 초기화하는 데 사용됩니다. 주로 final 필드와 같이 한 번만 초기화 가능한 필드를 설정할 때 유용합니다.

예시:

class Circle {
  final double radius;
  final double area;

  // 초기화 리스트를 사용한 생성자
  Circle(this.radius) : area = 3.14 * radius * radius;

  void showArea() {
    print('반지름: $radius, 면적: $area');
  }
}

void main() {
  Circle circle = Circle(5);
  circle.showArea();
}

출력:

반지름: 5.0, 면적: 78.5

5. 팩토리 생성자 (Factory Constructor)

팩토리 생성자는 새로운 인스턴스를 생성하는 대신 기존의 인스턴스를 반환하거나 특정 조건에 따라 객체를 생성해야 할 때 사용됩니다.

예시:

class Singleton {
  static final Singleton _instance = Singleton._internal();

  // private 생성자
  Singleton._internal();

  // 팩토리 생성자
  factory Singleton() {
    return _instance;
  }
}

void main() {
  var obj1 = Singleton();
  var obj2 = Singleton();

  print(obj1 == obj2); // true (같은 객체를 반환)
}

출력:

true

생성자를 사용할 때 주의할 점

  1. 매개변수 순서와 이름에 주의: 필드 이름과 매개변수 이름이 같을 때는 this를 사용하여 혼동을 피합니다.
  2. final 또는 const 필드 초기화: final 필드는 반드시 생성자를 통해 초기화해야 하며, const 필드는 컴파일 시점에 값이 결정되어야 합니다.
  3. 생성자와 메서드 구분: 생성자는 반환 타입이 없으며, 클래스와 동일한 이름을 가집니다.
반응형
반응형


Dart는 Flutter 앱 개발에 사용되는 언어로, 다양한 변수 타입을 제공합니다. 프로그래밍을 배우는 데 있어, 변수 타입을 이해하는 것은 매우 중요합니다. 변수는 데이터를 저장하고 조작하는 기본적인 단위이기 때문입니다. 이번 글에서는 Dart에서 제공하는 주요 내장 변수 타입에 대해 초보자들도 쉽게 이해할 수 있도록 설명하겠습니다.


1. 숫자 타입 (int, double)

Dart에서 숫자는 두 가지 주요 타입으로 나눠집니다: 정수실수입니다.

  • int: 정수형 변수로, 소수점이 없는 숫자를 저장합니다.
  • double: 실수형 변수로, 소수점을 포함하는 숫자를 저장합니다.

예시:

void main() {
  int age = 25;        // 정수형 변수
  double price = 10.99; // 실수형 변수
  
  print(age);   // 출력: 25
  print(price); // 출력: 10.99
}

주요 특징:

  • int는 소수점 없이 정수만 저장할 수 있습니다.
  • double은 소수점이 포함된 숫자, 예를 들어 10.99 같은 값을 저장할 수 있습니다.

2. 문자열 타입 (String)

**String**은 문자나 단어, 문장 등을 저장하는 변수 타입입니다. 즉, 텍스트 데이터를 다룰 때 사용됩니다.

예시:

void main() {
  String greeting = "Hello, Flutter!";
  
  print(greeting); // 출력: Hello, Flutter!
}

주요 특징:

  • 문자열은 큰따옴표(")나 작은따옴표(')로 감쌀 수 있습니다.
  • 여러 줄에 걸친 문자열을 사용할 때는 ''' 또는 **"""**를 사용할 수 있습니다.

3. 불리언 타입 (bool)

**bool**은 참(true) 또는 거짓(false) 값을 저장하는 변수 타입입니다. 주로 조건문(if, else)이나 논리 연산에서 사용됩니다.

예시:

void main() {
  bool isFlutterFun = true;
  bool isCold = false;
  
  print(isFlutterFun); // 출력: true
  print(isCold);       // 출력: false
}

주요 특징:

  • bool 타입은 true 또는 false 두 값만 가질 수 있습니다.
  • 주로 조건문에서 사용되어 프로그램 흐름을 제어합니다.

4. 리스트 타입 (List)

**List**는 여러 개의 값을 순서대로 저장하는 배열 같은 역할을 합니다. 리스트는 다양한 데이터 타입의 값을 하나의 변수에 저장할 수 있는 유용한 타입입니다.

예시:

void main() {
  List<String> fruits = ["Apple", "Banana", "Orange"];
  
  print(fruits); // 출력: [Apple, Banana, Orange]
  print(fruits[0]); // 출력: Apple
}

주요 특징:

  • 리스트는 **대괄호([])**로 묶어서 여러 값을 저장합니다.
  • 인덱스를 사용해 리스트의 특정 항목에 접근할 수 있습니다. 인덱스는 0부터 시작합니다.

5. 맵 타입 (Map)

**Map**은 **키(key)**와 **값(value)**을 쌍으로 저장하는 데이터 타입입니다. 리스트가 순서대로 저장되는 반면, 맵은 키를 통해 값을 검색하는 방식입니다.

예시:

void main() {
  Map<String, String> capitals = {
    'Korea': 'Seoul',
    'Japan': 'Tokyo',
    'USA': 'Washington, D.C.'
  };
  
  print(capitals); // 출력: {Korea: Seoul, Japan: Tokyo, USA: Washington, D.C.}
  print(capitals['Korea']); // 출력: Seoul
}

주요 특징:

  • 맵은 **중괄호({})**로 묶어서 키-값 쌍을 저장합니다.
  • 를 사용해 에 접근할 수 있습니다. 예를 들어, capitals['Korea']는 Seoul을 반환합니다.

6. 동적 타입 (dynamic)

**dynamic**은 어떤 타입이든 저장할 수 있는 타입입니다. 변수의 타입을 고정하지 않고, 실행 중에 변경할 수 있기 때문에 유연하게 사용할 수 있지만, 타입 안전성이 떨어질 수 있어 주의해서 사용해야 합니다.

예시:

void main() {
  dynamic something = 42;  // 정수 저장
  print(something);        // 출력: 42
  
  something = "Flutter";   // 문자열로 변경
  print(something);        // 출력: Flutter
}

주요 특징:

  • dynamic은 타입을 동적으로 결정하기 때문에 변수에 어떤 값이든 저장할 수 있습니다.
  • 타입을 명확히 정의하지 않기 때문에 타입 오류를 쉽게 발견할 수 없습니다. 이를 신중하게 사용해야 합니다.

7. Set 타입

**Set**은 중복되지 않는 값을 저장하는 집합을 나타내는 자료형입니다. 리스트와 유사하지만, 값의 순서는 보장하지 않으며, 중복된 값을 허용하지 않습니다. 주로 유일한 값들을 다뤄야 할 때 유용합니다.

void main() {
  Set<String> fruits = {"Apple", "Banana", "Orange", "Apple"};
  
  print(fruits); // 출력: {Apple, Banana, Orange}
  
  fruits.add("Grapes");  // 새로운 요소 추가
  print(fruits); // 출력: {Apple, Banana, Orange, Grapes}
}

주요 특징:

  • 중복되지 않는 값을 저장합니다. 예를 들어 "Apple"을 두 번 추가해도 한 번만 저장됩니다.
  • 순서가 보장되지 않습니다. 즉, 값이 저장되는 순서와 출력되는 순서는 다를 수 있습니다.
  • Set은 **add()**나 remove() 메서드를 사용하여 값을 추가하거나 삭제할 수 있습니다.
  • 검색 및 포함 여부 체크가 빠른 편입니다.

Set의 사용 예:

  • 고유한 값 목록을 관리해야 할 때 유용합니다.
  • 예를 들어, 사용자가 방문한 페이지 목록, 중복된 이메일을 처리하는 시스템 등에서 활용됩니다.

8. Enum 타입

Enum(열거형)은 여러 개의 관련 상수 값을 그룹화하여 하나의 타입으로 묶을 때 사용됩니다. Enum을 사용하면 값의 집합을 명확하게 정의하고, 코드에서 의미 있는 이름을 사용할 수 있어 가독성과 유지보수성이 높아집니다.

예시:

void main() {
  var status = Status.pending;
  
  switch (status) {
    case Status.pending:
      print("Processing...");
      break;
    case Status.completed:
      print("Completed!");
      break;
    case Status.failed:
      print("Failed!");
      break;
  }
}

enum Status {
  pending,   // 대기 중
  completed, // 완료됨
  failed     // 실패
}
반응형

'Flutter' 카테고리의 다른 글

다트 이해하기 - (4) Constructor(생성자)  (0) 2024.12.01
다트 이해하기 - (3) 위젯이란?  (0) 2024.12.01
다트 이해하기 - (1) 변수  (0) 2024.12.01
플러터 기본위젯  (0) 2024.03.30
typedef와 함수  (0) 2024.03.30
반응형

1. 플러터에서 변수란?

플러터(Flutter)는 Dart 언어를 기반으로 동작하며, Dart에서도 변수는 데이터를 저장하고 관리하기 위한 기본 요소입니다. Dart의 변수는 프로그램 내에서 값들을 저장하고 이를 조작하는 데 사용됩니다. Flutter를 사용한 UI 구성이나 상태 관리에서도 변수는 필수적입니다.


2. 변수 선언과 초기화

Dart에서 변수를 선언하려면 var, final, 또는 const 키워드를 주로 사용합니다. 또한, 변수의 자료형을 명시적으로 선언할 수도 있습니다.

1. var 키워드

  • Dart는 변수에 저장된 값에 따라 자료형을 자동으로 추론합니다.
  • 한 번 값이 할당되면 같은 자료형의 값만 저장 가능합니다.
void main() {
  var name = "가디사는 개발자 주녘"; // Dart가 자료형을 String으로 추론
  print(name); // 출력: 가디사는 개발자 주녘
}

 

2. 자료형 명시

  • 변수의 자료형을 명확히 정의할 수도 있습니다.
void main() {
  String name = "Dart"; // 명시적으로 자료형 선언
  int age = 25;         // 정수 자료형
  double height = 5.9;  // 실수 자료형
  bool isFlutterDev = true; // 불리언 자료형
  print("$name, $age, $height, $isFlutterDev");
}

 

3. final과 const

  • final: 런타임에 값이 결정되며, 이후 변경 불가능.
  • const: 컴파일 타임에 값이 결정되며, 상수로 사용.
void main() {
  final String appName = "MyApp"; // 실행 중 할당된 값은 변경 불가능
  const String framework = "Flutter"; // 컴파일 시점에 값이 고정
  print("$appName uses $framework.");
}

 


3.  변수 사용 시 주의점

  1. 변수의 범위(Scope) 이해하기
    • 변수가 선언된 위치에 따라 접근 가능한 범위가 달라집니다.
    • 예를 들어, 함수 내에서 선언된 변수는 함수 외부에서 접근할 수 없습니다.
  2. 변수 이름은 명확하게
    • 변수 이름은 역할을 명확히 나타내야 가독성이 높아집니다.
    • 예: counter는 "카운트" 용도로 사용되는 변수임을 바로 알 수 있습니다.
  3. 불필요한 변수 선언 피하기
    • 너무 많은 변수를 선언하면 코드가 복잡해질 수 있으니 꼭 필요한 변수만 선언해야합니다.

4. 플러터에서 변수의 중요성

플러터에서 변수는 다음과 같은 다양한 곳에서 사용됩니다:

  1. UI 상태 관리 (Stateful Widget과 State 클래스)
  2. 사용자 입력값 저장
  3. API 호출 결과 데이터 저장
  4. 애니메이션 상태 추적

5. Dart의 Null Safety란?

Null Safety는 Dart 언어의 중요한 기능으로, 개발자가 **null 값으로 인해 발생할 수 있는 오류(null reference errors)**를 방지할 수 있도록 돕는 메커니즘입니다. Null Safety는 Dart 2.12 버전부터 기본적으로 활성화되었으며, 플러터 앱 개발에서도 핵심적인 역할을 합니다.


6. 왜 Null Safety가 필요한가?

null 값은 변수에 아무 값도 저장되지 않은 상태를 나타냅니다. 하지만 null 값에 접근하거나 조작하려 하면 프로그램이 오류를 일으킵니다. 이를 Null Pointer Exception이라고 부르며, 많은 프로그래밍 언어에서 흔히 발생하는 문제입니다.

예를 들어:

String? name; // name은 null일 수 있음
print(name.length); // NullPointerException 발생

Dart의 Null Safety는 null이 될 수 있는 변수와 null이 될 수 없는 변수를 명확히 구분함으로써 이런 문제를 방지합니다.

 


7. Null Safety의 주요 개념

1. null이 될 수 없는 변수

기본적으로 Dart에서 선언된 변수는 null 값을 가질 수 없습니다. 이를 non-nullable 변수라고 합니다.

void main() {
  String name = "Flutter"; // null이 될 수 없음
  print(name); // 정상 출력: Flutter

  name = null; // 컴파일 오류 발생
}

 

2. null이 될 수 있는 변수

변수 뒤에 ?를 붙이면 해당 변수는 nullable이 되어 null 값을 가질 수 있습니다.

void main() {
  String? name; // null이 될 수 있음
  print(name); // 출력: null

  name = "Dart";
  print(name); // 출력: Dart
}

8. Null Safety를 사용하는 방법

1. null 값 확인

null이 될 수 있는 변수에 접근할 때는 반드시 null 여부를 확인해야 합니다.

예: null 체크

void main() {
  String? name;
  if (name != null) {
    print(name.length); // name이 null이 아닐 때만 length 접근
  }
}

 

예: null 병합 연산자 ??

?? 연산자는 null일 경우 대체 값을 반환합니다.

void main() {
  String? name;
  print(name ?? "No Name"); // 출력: No Name
}

2. null 값 사용 강제

! 연산자를 사용하면 null 값을 사용할 수 있다고 Dart에 강제로 알릴 수 있습니다. 이는 개발자가 null이 아님을 확신할 때만 사용해야 합니다.

⚠️ 잘못 사용하면 런타임 오류를 발생시킬 수 있으니 주의가 필요합니다.
void main() {
  String? name;
  name = "Flutter";
  print(name!.length); // name이 null이 아님을 보장
}

3. late 키워드

late 키워드는 변수를 나중에 초기화하겠다는 것을 Dart에 명시적으로 알립니다. null이 아닌 값을 보장해야 할 때 유용합니다.

void main() {
  late String name; // 초기값 없이 선언
  name = "Dart"; // 나중에 초기화
  print(name); // 출력: Dart
}

 


4. nullable 리스트

리스트에도 null safety가 적용됩니다. 예를 들어:

  • List<String>: null 값을 포함하지 않는 문자열 리스트
  • List<String?>: null 값을 포함할 수 있는 문자열 리스트
void main() {
  List<String> names = ["Alice", "Bob"];
  List<String?> nullableNames = ["Alice", null, "Bob"];

  print(names); // 출력: [Alice, Bob]
  print(nullableNames); // 출력: [Alice, null, Bob]
}

Null Safety가 Flutter에서 중요한 이유

플러터 앱 개발에서는 상태 관리, 사용자 입력 처리, 비동기 작업 등에서 null이 자주 발생할 수 있습니다. Null Safety는 다음과 같은 문제를 예방합니다:

  1. Widget 초기화 오류 방지
    위젯이나 상태(State)를 초기화할 때 null 값이 의도치 않게 사용되는 경우를 방지합니다.
  2. 비동기 작업에서 null 값 처리
    API 호출이나 데이터베이스에서 데이터를 가져올 때 null 값이 반환될 가능성을 처리할 수 있습니다.
  3. 안정성과 유지보수성 향상
    null로 인해 발생하는 오류를 컴파일 시점에서 잡아주므로 앱의 안정성이 향상됩니다.

 

요약: Null Safety의 장점

  • 컴파일 시점에서 null 관련 오류 탐지
    앱 실행 전에 null로 인해 발생할 수 있는 문제를 예방합니다.
  • 코드 안정성과 신뢰성 향상
    명확한 nullable 및 non-nullable 구분으로 오류를 줄입니다.
  • 가독성과 유지보수성 향상
    변수의 null 가능성을 코드에서 쉽게 확인할 수 있습니다.
반응형

'Flutter' 카테고리의 다른 글

다트 이해하기 - (3) 위젯이란?  (0) 2024.12.01
다트 이해하기 - (2) 내장 변수타입  (0) 2024.12.01
플러터 기본위젯  (0) 2024.03.30
typedef와 함수  (0) 2024.03.30
dart 이론  (0) 2024.03.30
반응형

1. 앱 아키텍처란?

앱 아키텍처는 애플리케이션의 전반적인 구조와 구성요소, 그리고 이러한 구성요소 간의 관계와 상호작용을 정의하는 청사진 또는 설계 원칙을 의미합니다. 쉽게 말해, 앱을 구축하는 데 필요한 구성요소와 이러한 구성요소들이 어떻게 함께 작동하고 연결되는지를 설명하는 방법론이라고 할 수 있습니다.

MVC, MVP, MVVM, VIPER 등 너무나 많은 아키텍처가 존재 한다. 하지만 원리는 거의 동일 하다.

역할 별로 레이어를 나눈다.
각 레이어는 각자의 역할에만 집중하게 설계하고 자신 밖에 업무에서 가능한 신경을 끈다.
각 레이어를 나누게 되면 수정 및 테스트 유지 보수가 용이하다.

 

 

2. MVC (Model-View-Controller) 패턴을 사용하는 주된 이유는?

MVC (Model-View-Controller) 패턴은 오랫동안 사용되어온 소프트웨어 디자인 패턴 중 하나입니다. 웹 애플리케이션, 데스크톱 애플리케이션, 그리고 최근에는 모바일 애플리케이션에서도 널리 사용됩니다.

  1. 가독성: 각 구성요소(Model, View, Controller)가 독립적이기 때문에 코드의 구조가 명확해져서 가독성이 향상됩니다.
  2. 확장성: MVC 패턴은 기능의 확장이 필요할 때, 해당하는 부분만을 수정하면 되기 때문에 확장성이 좋습니다. 예를 들면, UI를 변경하고자 할 때 View만 수정하면 되고, 데이터 처리 방식을 변경하고자 할 때는 Model만 수정하면 됩니다.
  3. 재사용성: 각 구성요소가 독립적이므로 재사용이 용이합니다. 특히 Model은 다른 시스템이나 프로젝트에서도 재사용할 수 있습니다.
  4. 분리와 집중: 각 구성요소의 역할이 분리되어 있기 때문에 각 역할에 집중할 수 있습니다. 이로 인해 코드의 품질과 유지 보수성이 향상됩니다.

이러한 이유로 많은 소프트웨어 개발 프로젝트에서 MVC 패턴을 선호하고 사용합니다.

 

 

3. MVVM 패턴에 대해 알아 보자

  • Model: 실제 데이터 및 비즈니스 로직을 포함합니다. 데이터베이스 액세스, 웹 서비스 호출 등의 작업을 수행합니다.
  • View: 사용자에게 보여지는 UI 요소. 버튼, 텍스트박스, 라벨 등의 위젯 또는 컴포넌트를 포함합니다. View는 ViewModel에 직접적으로 연결되어 데이터를 가져옵니다.
  • ViewModel: Model과 View 사이의 중개자 역할을 합니다. Model에서 데이터를 가져오고 View에 표시할 준비를 담당합니다. 또한 View에서 이벤트를 수신하고 그에 따라 Model을 업데이트합니다.

 

 

1단계 : 시나리오 코드 1 - MV 패턴으로 코드 만들어 보기

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(
      home: HomeView(),
    );
  }
}

// View
class HomeView extends StatefulWidget {
  const HomeView({super.key});

  @override
  State<HomeView> createState() => _HomeViewState();
}

class _HomeViewState extends State<HomeView> {

  // 핵심 데이터 선정 - 클래스
  // Model 에서 데이터와 관련된 로직이 포함 된다.
  int _counter = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('뷰 모델 없는 코드를 작성해보기'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('간단한 뷰와 모델 예제'),
            Text('숫자 확인(핵심데이터) ${_counter}'),
            ElevatedButton(
              onPressed: () {
                // 사용자가 버튼을 클릭하면 숫자가 올라가는 기능을 만들자
                setState(() {
                  _counter++;
                });
              },
              child: Text('증감'),
            )
          ],
        ),
      ),
    );
  }
}

증감 버튼을 클릭시 숫자가 증가하는 것을 볼 수 있음

 

1단계에서는 MV 개념으로 코딩을 했다면 ViewModel 이라는 개념을 적용해보자

 

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(
      home: HomeView(),
    );
  }
}

// View
class HomeView extends StatefulWidget {
  const HomeView({super.key});

  @override
  State<HomeView> createState() => _HomeViewState();
}

class _HomeViewState extends State<HomeView> {

  // 뷰에서는 뷰 모델만 바라보면 된다.
  final CounterViewModel viewModel = CounterViewModel();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('뷰 모델 없는 코드를 작성해보기'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('간단한 뷰와 모델 예제'),
            Text('숫자 확인(핵심데이터) ${viewModel.counter}'),
            ElevatedButton(
              onPressed: () {
                // 사용자가 버튼을 클릭하면 숫자가 올라가는 기능을 만들자
                setState(() {
                 viewModel.incrementCounter();
                });
              },
              child: Text('증감'),
            )
          ],
        ),
      ),
    );
  }
}


// 1단계에서는 MV 개념으로 코딩을 했다면 ViewModel 이라는 개념을 적용해보자
// viewModel
class CounterViewModel {
  int _counter = 0;
  int get counter => _counter;

  void incrementCounter(){
    _counter++;
  }
}
반응형

'Flutter' 카테고리의 다른 글

dart 이론  (0) 2024.03.30
TodoList App 만들기  (0) 2024.03.21
dart 비동기 프로그래밍 - 3  (0) 2024.03.15
dart 비동기 프로그래밍 - 2  (0) 2024.03.15
dart 비동기 프로그래밍 -1  (0) 2024.03.14
반응형


프로젝트 명 : blog_web_app

개발 환경 : 플러터 SDK : 3.3.x

미션 : 웹뷰를 사용해서 웹사이트를 앱으로 포장해보기

기능 : 웹뷰를 사용해서 앱에서 웹사이트 실행하기

핵심 구성요소 : StatelessWideget, AppBar, WebView, IconButton

플러그인 : webview_flutter: 3.0.4


📝 콜백 함수

콜백함수란 일정 작업이 완료되면 실행되는 함수를 말합니다.

함수를 정의해두면 바로 실행되지 않고 특정 조건이 성립될 때 실행되기 때문에 콜백이라고 합니다.

 

📝 코딩해보기

1) pubspec.yaml 설정

pubspec.yaml 파일은 플러터 프로젝트와 관련된 설정을 하는 파일입니다.

dependencies:
  flutter:
    sdk: flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.2
  webview_flutter: 3.0.4	// 여기에 추가

 

2) 안드로이드 설정

android/app/src/main/AndroidManifest.xml 파일 수정

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET"/>	// 여기에 추가
    <application
        android:label="blog_web_app"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">

 

3) build.gradle 파일 수정

android {
    compileSdkVersion 32	// 여기 추가
    namespace "com.example.blog_web_app"
    compileSdkVersion flutter.compileSdkVersion
    ndkVersion flutter.ndkVersion

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = '1.8'
    }

    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
    }

    defaultConfig {
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
        applicationId "com.example.blog_web_app"
        // You can update the following values to match your application needs.
        // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
        minSdkVersion 20	// 여기 추가
        targetSdkVersion flutter.targetSdkVersion
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }

 

4) iOS 설정

// 아래 코드 추가
<key>NSAppTransportSecurity</key>
        <dict>
            <key>NSAllowsLocalNetworking</key>
            <true/>
            <key>NSAllowArbitraryLoadsInWebContent</key>
            <true/>
        <dict/>
        // 여기까지
</dict>
</plist>

 

5) 프로젝트 초기화

lib 폴더 위에 마우스 우클릭 > screen 폴더 생성 > 폴더 안에 home_screen.dart 파일 생성

import 'package:flutter/material.dart';

import 'package:webview_flutter/webview_flutter.dart';

class HomeScreen extends StatelessWidget {
  // 컨트롤러 변수 생성
  WebViewController? controller;

  HomeScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        // 배경색 지정
        backgroundColor: Colors.orange,
        // 앱 타이틀 설정
        title: Text('준혁의 Blog'),
        // 가운데 정렬
        centerTitle: true,

        // AppBar의 actions 매개 변수
         actions: [
           IconButton(
             // 눌렀을 때 콜백 함수 설정
             onPressed: () {
               if (controller != null) {
                 // 웹 뷰에서 보여줄 사이트 실행하기
                 controller!.loadUrl('https://devjunyeok.tistory.com/');
               }
             },
             // 홈 버튼 아이콘 설정
             icon: Icon(
               Icons.home,
             ),
           )
         ],
      ),
      body: WebView(
        // 웹뷰 생성 함수
        onWebViewCreated: (WebViewController controller) {
          this.controller = controller; // 위젯에 컨트롤러 저장
        },
        initialUrl: 'https://devjunyeok.tistory.com/',
        javascriptMode: JavascriptMode.unrestricted,
      ),
    );
  }
}

 

6) main.dart 파일 수정

import 'package:blog_web_app/screen/home_screen.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      home: HomeScreen(),
    ),
  );
}

 

7) 실행결과

반응형

'Flutter' 카테고리의 다른 글

TodoList App 만들기  (0) 2024.03.21
Flutter MVVM 패턴 - 1  (0) 2024.03.19
dart 비동기 프로그래밍 - 3  (0) 2024.03.15
dart 비동기 프로그래밍 - 2  (0) 2024.03.15
dart 비동기 프로그래밍 -1  (0) 2024.03.14

+ Recent posts