728x90
반응형
728x90
반응형
728x90
반응형

다트(Dart) 프로젝트를 시작하기 위한 다양한 템플릿(template) 옵션

Dart의 dart create 명령어는 다양한 프로젝트 템플릿을 지원하여 개발자가 목적에 맞는 프로젝트를 쉽게 시작할 수 있도록 돕습니다.


1. console

  • 설명: 의존성을 포함한 간단한 콘솔 애플리케이션을 생성합니다.
  • 특징: console-simple보다 더 구조화된 프로젝트이며, 테스트 파일이 추가로 포함됩니다.
  • 명령어:
    dart create -t console project_name
    

2. package

  • 설명: Dart 패키지 프로젝트를 생성합니다. 라이브러리를 개발할 때 적합합니다.
  • 특징:
    • lib 디렉토리가 기본 포함.
    • 테스트를 위한 기본 설정이 포함됨.
  • 명령어:
    dart create -t package project_name
    

3. package-simple

  • 설명: 최소 설정으로 Dart 패키지를 생성합니다.
  • 특징: 의존성 관리 없이 간단한 라이브러리 패키지를 만들 때 사용.
  • 명령어:
    dart create -t package-simple project_name
    

4. server-shelf

  • 설명: Shelf 패키지를 사용한 간단한 HTTP 서버 애플리케이션을 생성합니다.
  • 특징:
    • HTTP 서버 코드가 기본 포함.
    • pubspec.yaml에 Shelf 패키지가 추가됨.
  • 명령어:
    dart create -t server-shelf project_name
    

5. web

  • 설명: 웹 애플리케이션 프로젝트를 생성합니다.
  • 특징: Dart 웹 관련 패키지와 구조가 포함됩니다.
  • 명령어:
    dart create -t web project_name
    

6. flutter (Flutter SDK 필요)

  • 설명: Flutter 애플리케이션 프로젝트를 생성합니다.
  • 명령어:
    flutter create project_name
    

템플릿 요약

  • 콘솔 템플릿: console, console-simple
  • 패키지 템플릿: package, package-simple
  • 서버 템플릿: server-shelf
  • 웹 템플릿: web
  • Flutter 템플릿: flutter
728x90
반응형
728x90
반응형

Flutter 반복되지 않는 랜덤(Random) 확률 생성

Flutter에서 앱 실행 시마다 서로 다른 랜덤 확률을 생성하려면 Random 클래스를 사용하거나, 시간을 기반으로 고유한 값을 생성하는 방법을 활용할 수 있습니다. 아래에서는 이를 구현하는 다양한 방법을 소개합니다.


1. Random 클래스를 사용

dart:math 라이브러리의 Random 클래스를 사용할 때 시드(seed)를 설정하지 않으면, 실행할 때마다 서로 다른 랜덤 값이 생성됩니다.

dart

import 'dart:math';

void main() {
  final random = Random(); // 시드 없이 Random 객체 생성
  final randomValue = random.nextDouble(); // 0.0 ~ 1.0 사이의 랜덤 값
  print('랜덤 확률: $randomValue');
}

2. 고유한 시드 값으로 초기화

시간을 기반으로 고유 시드 값을 생성하면 더 고유한 랜덤 값을 보장할 수 있습니다.

dart

import 'dart:math';

void main() {
  final seed = DateTime.now().millisecondsSinceEpoch; // 현재 시간을 시드로 사용
  final random = Random(seed); 
  final randomValue = random.nextDouble();
  print('랜덤 확률: $randomValue');
}

3. 앱 구동 시 고정되지 않는 확률 생성

앱이 실행될 때마다 다른 확률을 사용해야 할 경우, initState나 앱 초기화 로직에서 위 방식을 적용하면 됩니다.

dart

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

class RandomProbabilityWidget extends StatefulWidget {
  @override
  _RandomProbabilityWidgetState createState() =>
      _RandomProbabilityWidgetState();
}

class _RandomProbabilityWidgetState extends State<RandomProbabilityWidget> {
  late double randomProbability;

  @override
  void initState() {
    super.initState();
    final random = Random(); // 시드 없이 Random 생성
    randomProbability = random.nextDouble(); // 0.0 ~ 1.0 사이의 확률 생성
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text('랜덤 확률: $randomProbability'),
    );
  }
}

void main() {
  runApp(MaterialApp(home: Scaffold(body: RandomProbabilityWidget())));
}
728x90
반응형
728x90
반응형

Flutter로 구현하는 로그인 및 비밀번호 복구 화면

  • 다음은 Flutter를 사용해 로그인 화면과 비밀번호 복구 화면을 구현하는 코드입니다.
  • 이 코드는 이메일 및 비밀번호 입력에 대한 유효성 검사를 포함하며, 비밀번호 복구 화면으로 네비게이션하는 기능을 제공합니다.

전체 Flutter 코드

dart

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: '로그인 화면',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: LoginScreen(),
    );
  }
}

class LoginScreen extends StatefulWidget {
  @override
  _LoginScreenState createState() => _LoginScreenState();
}

class _LoginScreenState extends State<LoginScreen> {
  final _formKey = GlobalKey<FormState>();
  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();

  void _login() {
    if (_formKey.currentState!.validate()) {
      // 로그인 로직을 여기에 구현
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('로그인 성공')),
      );
    }
  }

  void _navigateToForgotPassword() {
    Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => ForgotPasswordScreen()),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('로그인'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Form(
          key: _formKey,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              // 이메일 입력 필드
              TextFormField(
                controller: _emailController,
                decoration: InputDecoration(
                  labelText: '이메일',
                  border: OutlineInputBorder(),
                ),
                keyboardType: TextInputType.emailAddress,
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return '이메일을 입력하세요';
                  }
                  if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) {
                    return '유효한 이메일 주소를 입력하세요';
                  }
                  return null;
                },
              ),
              SizedBox(height: 16),
              // 비밀번호 입력 필드
              TextFormField(
                controller: _passwordController,
                decoration: InputDecoration(
                  labelText: '비밀번호',
                  border: OutlineInputBorder(),
                ),
                obscureText: true,
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return '비밀번호를 입력하세요';
                  }
                  if (value.length < 6) {
                    return '비밀번호는 6자 이상이어야 합니다';
                  }
                  return null;
                },
              ),
              SizedBox(height: 16),
              // 비밀번호 복구로 이동 버튼
              Row(
                mainAxisAlignment: MainAxisAlignment.end,
                children: [
                  TextButton(
                    onPressed: _navigateToForgotPassword,
                    child: Text('비밀번호를 잊으셨나요?'),
                  ),
                ],
              ),
              SizedBox(height: 16),
              // 로그인 버튼
              ElevatedButton(
                onPressed: _login,
                child: Text('로그인'),
                style: ElevatedButton.styleFrom(
                  minimumSize: Size(double.infinity, 48),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class ForgotPasswordScreen extends StatelessWidget {
  final TextEditingController _emailController = TextEditingController();

  void _resetPassword(BuildContext context) {
    final email = _emailController.text;
    if (email.isEmpty || !RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(email)) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('유효한 이메일 주소를 입력하세요')),
      );
      return;
    }
    // 비밀번호 복구 로직 구현
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('$email 로 비밀번호 복구 링크가 전송되었습니다')),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('비밀번호 복구'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              '비밀번호를 복구하려면 이메일 주소를 입력하세요',
              style: TextStyle(fontSize: 16),
              textAlign: TextAlign.center,
            ),
            SizedBox(height: 16),
            TextField(
              controller: _emailController,
              decoration: InputDecoration(
                labelText: '이메일',
                border: OutlineInputBorder(),
              ),
              keyboardType: TextInputType.emailAddress,
            ),
            SizedBox(height: 16),
            ElevatedButton(
              onPressed: () => _resetPassword(context),
              child: Text('비밀번호 복구'),
              style: ElevatedButton.styleFrom(
                minimumSize: Size(double.infinity, 48),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

주요 기능 설명

  1. 로그인 화면:

    • 이메일과 비밀번호 입력 필드를 제공하며 유효성 검사를 통해 입력 데이터를 검증합니다.
    • "로그인" 버튼으로 입력 데이터를 확인하고, 스낵바를 통해 결과를 보여줍니다.
    • "비밀번호를 잊으셨나요?" 버튼을 통해 비밀번호 복구 화면으로 이동합니다.
  2. 비밀번호 복구 화면:

    • 이메일 입력 필드와 유효성 검사 로직을 포함합니다.
    • 입력된 이메일로 비밀번호 복구 요청을 처리하는 버튼을 제공합니다.
  • Flutter로 간단하면서도 유효성 검사와 네비게이션이 포함된 로그인 및 비밀번호 복구 화면을 구현할 수 있습니다.
728x90
반응형
728x90
반응형

Dart의 Isolate와 유사한 기능을 제공하는 언어

Dart의 Isolate는 독립된 메모리 공간과 메시지 패싱을 통해 동작하며, 병렬 처리를 안전하고 효율적으로 수행할 수 있습니다.

다른 언어의 유사한 기능과 비교하기 전에, Dart Isolate의 기본적인 작동 방식을 살펴보겠습니다.


Dart의 Isolate

설명

Dart의 Isolate는 각자 독립적인 메모리 공간에서 실행되며, 데이터를 공유하지 않습니다.

데이터를 주고받기 위해 SendPortReceivePort를 사용합니다. 아래는 간단한 예제입니다.

예제 코드

dart

import 'dart:isolate';

void isolateFunction(SendPort sendPort) {
  sendPort.send("Hello from Isolate!");
}

void main() async {
  // ReceivePort 생성
  final receivePort = ReceivePort();

  // Isolate 생성 및 실행
  await Isolate.spawn(isolateFunction, receivePort.sendPort);

  // 메시지 수신
  receivePort.listen((message) {
    print("Main received: $message");
    receivePort.close(); // 리소스 해제
  });
}

출력

Main received: Hello from Isolate!

설명

  1. ReceivePort: 데이터를 받을 수 있는 포트를 생성합니다.
  2. SendPort: 다른 Isolate에 데이터를 보낼 때 사용합니다.
  3. Isolate.spawn을 통해 새로운 Isolate를 생성하고, 작업을 수행합니다.

Dart의 Isolate와 유사한 기능을 제공하는 언어와 예제

이제 다른 언어에서 Dart의 Isolate와 유사한 기능을 제공하는 방식과 예제를 살펴보겠습니다.


1. Erlang / Elixir

Erlang의 Actor 모델

erlang

% Spawn a new process
Pid = spawn(fun() -> loop() end).

% Send a message to the process
Pid ! {self(), "Hello"}.

% Receive the message
receive
    {From, Message} -> io:format("Received ~p from ~p~n", [Message, From])
end.

% Looping function
loop() ->
    receive
        Message -> io:format("Processing ~p~n", [Message]),
        loop()
    end.

Elixir 버전

elixir

spawn(fn ->
  receive do
    msg -> IO.puts("Received message: #{msg}")
  end
end)
|> send("Hello Elixir")

2. Go (Goroutine + Channel)

go

package main

import "fmt"

func worker(msgChan chan string) {
    msg := <-msgChan
    fmt.Println("Received:", msg)
}

func main() {
    msgChan := make(chan string)
    go worker(msgChan)
    msgChan <- "Hello Goroutine"
}

3. Rust (Message Passing)

rust

use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        tx.send("Hello Rust").unwrap();
    });

    let received = rx.recv().unwrap();
    println!("Received: {}", received);
}

4. Akka (Scala Actor Model)

scala

import akka.actor.{Actor, ActorSystem, Props}

class HelloActor extends Actor {
  def receive = {
    case msg: String => println(s"Received message: $msg")
  }
}

object Main extends App {
  val system = ActorSystem("HelloSystem")
  val helloActor = system.actorOf(Props[HelloActor], name = "helloactor")

  helloActor ! "Hello Akka"
}

5. JavaScript (Worker Thread)

javascript

// worker.js
self.onmessage = (e) => {
  console.log("Message received from main script:", e.data);
  self.postMessage("Hello Worker");
};

// main.js
const worker = new Worker("worker.js");

worker.postMessage("Hello from main");
worker.onmessage = (e) => {
  console.log("Message received from worker:", e.data);
};

6. Python (multiprocessing)

python

from multiprocessing import Process, Queue

def worker(queue):
    msg = queue.get()
    print(f"Received: {msg}")

if __name__ == "__main__":
    queue = Queue()
    p = Process(target=worker, args=(queue,))
    p.start()
    queue.put("Hello Python")
    p.join()

7. C# (Task Parallel Library with Channels)

csharp

using System;
using System.Threading.Channels;
using System.Threading.Tasks;

class Program
{
    static async Task Worker(ChannelReader<string> reader)
    {
        while (await reader.WaitToReadAsync())
        {
            var message = await reader.ReadAsync();
            Console.WriteLine($"Received: {message}");
        }
    }

    static async Task Main()
    {
        var channel = Channel.CreateUnbounded<string>();

        _ = Task.Run(() => Worker(channel.Reader));

        await channel.Writer.WriteAsync("Hello C#");
        channel.Writer.Complete();
    }
}

8. Clojure (core.async)

clojure

(require '[clojure.core.async :refer [chan go <! >!]])

(let [ch (chan)]
  (go (println "Received:" (<! ch)))
  (go (>! ch "Hello Clojure")))

Dart의 Isolate와 다른 언어의 유사 기능 비교

위 예제들은 모두 독립적인 실행 단위에서 메시지 패싱을 활용한 병렬 처리를 보여줍니다.

Dart의 Isolate와 비슷하게, 각 언어는 공유 메모리를 피하고 안전한 병렬 처리를 가능하게 합니다.

이를 활용해 효율적인 멀티스레드 또는 병렬 프로그램을 작성할 수 있습니다.

728x90
반응형
728x90
반응형

삼각형 내부 점 포함 검사 방법 및 최적화 방안

삼각형 내부에 점이 포함되어 있는지 검사하는 문제는 그래픽 처리, 물리 엔진, 충돌 검사 등 다양한 분야에서 중요하게 사용됩니다. Dart 언어를 사용하여 이 문제를 해결하는 두 가지 방법과 성능을 최적화할 수 있는 여러 기법을 아래에 종합했습니다.

1. 삼각형 내부에 점이 있는지 검사하는 방법

삼각형 내부에 점이 포함되어 있는지 확인하기 위해 벡터의 외적과 면적을 활용하는 두 가지 대표적인 방법을 사용할 수 있습니다.

1-1. 벡터의 외적을 이용한 방법

벡터의 외적은 두 벡터 사이의 방향을 계산하여 삼각형의 세 변에 대해 점이 같은 방향에 위치하는지를 검사하는 방식입니다. 삼각형의 꼭짓점을 A, B, C라고 하고, 검사할 점을 P라고 할 때, 다음과 같이 벡터 외적을 활용하여 검사할 수 있습니다.

dart

class Point {
  final double x;
  final double y;

  Point(this.x, this.y);
}

double crossProduct(Point a, Point b, Point c) {
  return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
}

bool isPointInTriangle(Point a, Point b, Point c, Point p) {
  double cross1 = crossProduct(a, b, p);
  double cross2 = crossProduct(b, c, p);
  double cross3 = crossProduct(c, a, p);

  return (cross1 >= 0 && cross2 >= 0 && cross3 >= 0) || (cross1 <= 0 && cross2 <= 0 && cross3 <= 0);
}

위 코드에서 crossProduct 함수는 벡터 간의 외적을 계산하며, 세 외적의 부호가 모두 같다면 점 P는 삼각형 ABC 내부에 있습니다.

1-2. 면적을 이용한 방법

삼각형 ABC의 전체 면적을 계산하고, 점 P와 각각의 꼭짓점이 이루는 작은 삼각형 세 개의 면적을 더하여 비교하는 방법입니다. 삼각형 ABC의 면적과 작은 삼각형들의 면적 합이 같다면 점은 삼각형 내부에 위치합니다.

dart

double triangleArea(Point a, Point b, Point c) {
  return ((a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y)) / 2.0).abs();
}

bool isPointInTriangleArea(Point a, Point b, Point c, Point p) {
  double areaABC = triangleArea(a, b, c);
  double areaABP = triangleArea(a, b, p);
  double areaBCP = triangleArea(b, c, p);
  double areaCAP = triangleArea(c, a, p);

  return areaABC == (areaABP + areaBCP + areaCAP);
}

2. 성능 최적화 기법

삼각형 내부 포함 검사의 성능을 향상시키기 위해 여러 최적화 기법을 적용할 수 있습니다. 특히, 반복적으로 검사를 수행하는 경우 이 최적화가 유용합니다.

2-1. 사전 계산 최적화

삼각형의 꼭짓점이 고정되어 변하지 않는 경우, 반복적인 연산을 줄이기 위해 삼각형의 각 변에 대한 방향을 사전에 계산해 둘 수 있습니다. 예를 들어, 벡터 외적을 계산한 후 그 방향을 기준으로만 비교할 수 있습니다.

2-2. Bounding Box(경계 상자) 검사

점이 삼각형 내부에 있는지 확인하기 전, 삼각형을 포함하는 최소한의 사각형(Bounding Box)을 계산하여 점이 이 경계 사각형 내부에 있는지 먼저 확인합니다. 점이 경계 사각형 외부에 있으면 추가적인 계산 없이 삼각형 외부에 있다고 판단할 수 있습니다.

dart

bool isPointInBoundingBox(Point a, Point b, Point c, Point p) {
  double minX = [a.x, b.x, c.x].reduce((value, element) => value < element ? value : element);
  double maxX = [a.x, b.x, c.x].reduce((value, element) => value > element ? value : element);
  double minY = [a.y, b.y, c.y].reduce((value, element) => value < element ? value : element);
  double maxY = [a.y, b.y, c.y].reduce((value, element) => value > element ? value : element);

  return (p.x >= minX && p.x <= maxX && p.y >= minY && p.y <= maxY);
}

위의 isPointInBoundingBox 함수로 Bounding Box 검사를 먼저 수행하여 내부에 있을 때만 삼각형 포함 여부를 검사하면, 불필요한 계산을 줄일 수 있습니다.

2-3. 오차 허용 범위 설정

컴퓨터의 실수 계산에서는 미세한 오차가 발생할 수 있으므로 == 연산자 대신 작은 오차 허용 범위를 설정하여 비교하는 것이 좋습니다. 면적을 이용한 방법에서 오차 허용 범위를 사용하여 보다 정확한 비교가 가능합니다.

dart

bool isAlmostEqual(double a, double b, double epsilon) {
  return (a - b).abs() < epsilon;
}

bool isPointInTriangleWithTolerance(Point a, Point b, Point c, Point p, double epsilon) {
  double areaABC = triangleArea(a, b, c);
  double areaABP = triangleArea(a, b, p);
  double areaBCP = triangleArea(b, c, p);
  double areaCAP = triangleArea(c, a, p);

  return isAlmostEqual(areaABC, areaABP + areaBCP + areaCAP, epsilon);
}

위 함수에서 epsilon을 작은 값(예: 1e-9)으로 설정하여 비교하면, 실수 연산에서 발생할 수 있는 미세한 차이로 인해 발생할 수 있는 오류를 줄일 수 있습니다.

2-4. 정규화된 방향 벡터 사용

벡터 외적 계산에서 방향 비교만 필요한 경우, 벡터를 정규화하여 계산을 단순화할 수 있습니다. 정규화된 벡터는 크기 비교가 필요하지 않으므로 부호만 검사해도 되며, 일부 연산을 최적화할 수 있습니다.

결론

Dart에서 삼각형 내부에 점이 포함되어 있는지 검사하는 방법으로 벡터 외적과 면적을 이용한 방법을 사용했으며, 성능을 최적화하기 위한 다양한 방법을 소개했습니다. 사전 계산, Bounding Box 검사, 오차 허용 범위 설정, 정규화된 방향 벡터 등을 적용하여 정확성과 효율성을 동시에 확보할 수 있습니다.

728x90
반응형
728x90
반응형

AdMob 테스트용 광고 ID 목록

광고 형식 데모 광고 단위 ID
앱 오프닝 광고 ca-app-pub-3940256099942544/9257395921
적응형 배너 ca-app-pub-3940256099942544/9214589741
고정 크기 배너 ca-app-pub-3940256099942544/6300978111
전면 광고 ca-app-pub-3940256099942544/1033173712
보상형 광고 ca-app-pub-3940256099942544/5224354917
보상형 전면 광고 ca-app-pub-3940256099942544/5354046379
네이티브 ca-app-pub-3940256099942544/2247696110
네이티브 동영상 ca-app-pub-3940256099942544/1044960115

더 자세한 내용은 Google AdMob 개발자 문서를 참고해 주세요.

728x90
반응형
728x90
반응형

Dart 예약어 late

late는 Dart 언어에서 변수를 나중에 초기화할 것을 선언할 때 사용하는 예약어입니다. Flutter는 Dart 언어를 기반으로 하기 때문에, late 키워드를 자주 사용하게 됩니다.

기본적으로 변수는 선언 시에 즉시 초기화되어야 하지만, late를 사용하면 변수를 선언할 때는 초기화를 하지 않고, 나중에 필요할 때 초기화할 수 있습니다. 이를 통해 지연된 초기화를 구현할 수 있습니다.

예시:

late String name;

void main() {
  name = "Flutter";
  print(name); // "Flutter" 출력
}

이렇게 late 키워드를 사용하면 변수를 처음 접근할 때 초기화가 이루어집니다. 다만, 변수를 접근하기 전에 초기화하지 않으면 오류가 발생할 수 있습니다.

728x90
반응형
728x90
반응형

Flutter에서 setState()의 역할과 UI 재빌드 과정

Flutter에서 setState()는 위젯의 상태가 변경될 때 그 변화를 UI에 반영하기 위한 메서드입니다. Flutter는 상태(State)를 기반으로 화면을 렌더링하는데, 상태가 변경되면 그에 맞춰 UI도 업데이트되어야 합니다. 이때 setState()를 사용하여 상태 변화가 발생했음을 Flutter에게 알리고, 이를 통해 UI를 다시 빌드합니다.

setState()의 동작 과정

  1. 상태 변화 감지: setState() 내부에서 상태를 변경합니다. 이는 상태 변화를 UI에 반영하기 위한 시작점입니다.

  2. 위젯 트리 재생성: 상태 변화가 일어나면 Flutter는 해당 변화를 반영하기 위해 위젯 트리를 다시 빌드합니다. 이 과정에서 상태에 따라 변경된 UI 요소를 재구성하며, 이때 위젯의 build() 메서드가 호출됩니다.

  3. 화면 업데이트: 재빌드된 위젯 트리에서 변경된 부분만 화면에 다시 그려집니다. Flutter는 매우 효율적으로 동작하여, 필요한 위젯만 다시 렌더링하므로 전체 앱이 다시 그려지지 않고 성능에도 영향을 덜 줍니다.

구체적인 예

예를 들어, 버튼을 눌러 카운터 값을 증가시키는 앱에서는 숫자를 표시하는 텍스트 위젯만 상태 변경에 따라 재빌드됩니다. 버튼이나 다른 요소들은 변경되지 않은 상태로 유지되며, Flutter는 이러한 차이를 효율적으로 관리합니다.

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Column(
      children: [
        Text('$counter'),  // 상태가 변경되면 이 텍스트만 재빌드됨
        ElevatedButton(
          onPressed: () {
            setState(() {
              counter++;
            });
          },
          child: Text('Increment'),
        ),
      ],
    ),
  );
}

이 코드에서 setState()counter 값을 증가시키고, 이에 따라 숫자를 표시하는 텍스트만 재빌드되어 화면에 반영됩니다.

결론적으로, setState()는 상태 변화를 UI에 반영하고 필요한 부분만 재렌더링하여 효율적인 화면 업데이트를 가능하게 합니다.

728x90
반응형
728x90
반응형

Dart의 일반 매개변수와 명명된 매개변수의 차이점

Dart에서 명명된 매개변수일반 매개변수는 함수나 생성자에 인자를 전달하는 방식에서 차이가 있습니다. 두 매개변수는 사용하는 방법과 그 목적에 따라 다릅니다. 차이점을 설명하겠습니다.

1. 일반 매개변수 (Positional Parameters)

일반 매개변수는 인자를 순서대로 전달해야 합니다. 함수 호출 시 매개변수를 정의된 순서에 맞게 입력해야 하며, 모든 매개변수가 필수로 제공됩니다.

예시

void printInfo(String name, int age) {
  print('Name: $name, Age: $age');
}

void main() {
  printInfo('John', 25);  // 순서대로 인자를 전달
}
  • 일반 매개변수는 인자의 순서가 중요합니다. 예를 들어, printInfo('John', 25)에서 먼저 name으로 'John'을 전달하고, 그다음 age로 25를 전달합니다.
  • 필수 매개변수로서 항상 값을 제공해야 합니다.

2. 명명된 매개변수 (Named Parameters)

명명된 매개변수는 중괄호 {}로 감싸서 정의되며, 인자의 순서를 상관하지 않고 이름을 통해 전달할 수 있습니다. 또한 명명된 매개변수는 선택적으로 만들 수 있으며, 기본값을 지정할 수 있습니다.

예시

void printInfo({String? name, int? age}) {
  print('Name: $name, Age: $age');
}

void main() {
  printInfo(name: 'John', age: 25);  // 이름으로 명시하여 인자 전달
  printInfo(age: 30);  // name은 생략 가능
}
  • 순서에 상관없이 인자를 전달할 수 있습니다. name: 'John'age: 25를 전달할 때 순서가 바뀌어도 문제가 없습니다.
  • 선택적 매개변수로 만들 수 있으며, 값을 제공하지 않으면 null 또는 기본값이 사용됩니다.
  • 매개변수의 기본값을 지정할 수 있습니다.
void printInfo({String name = 'Unknown', int age = 0}) {
  print('Name: $name, Age: $age');
}

void main() {
  printInfo();  // 기본값을 사용: Name: Unknown, Age: 0
}

3. 차이점 요약

구분 일반 매개변수 명명된 매개변수
인자 전달 방식 순서대로 전달 이름을 통해 전달 (순서 상관 없음)
필수 여부 기본적으로 필수 선택적, 기본값 설정 가능
사용법 순서를 맞춰야 함 이름으로 명시해서 전달, 일부 생략 가능
중괄호 사용 여부 사용하지 않음 중괄호 {}로 묶어 사용

4. 일반 매개변수와 명명된 매개변수를 함께 사용하는 방법

일반 매개변수와 명명된 매개변수를 함께 사용할 수도 있습니다. 일반 매개변수는 여전히 필수로 제공되지만, 명명된 매개변수는 선택적으로 사용할 수 있습니다.

void printInfo(String name, {int? age}) {
  print('Name: $name, Age: ${age ?? 'Unknown'}');
}

void main() {
  printInfo('John', age: 25);  // 일반 매개변수 + 명명된 매개변수
  printInfo('Doe');            // age 생략 가능
}

결론

  • 일반 매개변수순서가 중요하며, 필수로 전달해야 합니다.
  • 명명된 매개변수이름으로 인자를 전달할 수 있고, 선택적으로 사용 가능하며, 기본값을 설정할 수 있습니다.
728x90
반응형
728x90
반응형

MaterialApp 네비게이션 및 routes

MaterialApproutes는 Flutter에서 여러 화면(페이지) 간의 이동을 제어하고 관리하는 기능을 제공합니다. 이를 통해 특정 경로를 설정하고, 그 경로에 해당하는 화면을 미리 정의할 수 있습니다. 즉, 앱 내에서 사용자가 버튼을 클릭하거나 다른 UI 요소를 통해 화면을 이동할 때, 경로에 따라 적절한 화면을 보여주는 역할을 합니다.

routes는 경로를 키로, 해당 경로에 표시될 위젯을 값으로 가지는 맵(Map) 형태로 설정됩니다. 예를 들어, 아래와 같이 사용할 수 있습니다:

MaterialApp(
  routes: {
    '/': (context) => HomePage(),
    '/second': (context) => SecondPage(),
    '/third': (context) => ThirdPage(),
  },
)

이 코드는 '/' 경로에서 HomePage, '/second' 경로에서 SecondPage, '/third' 경로에서 ThirdPage가 각각 표시되도록 설정한 것입니다.

화면 간 이동은 Navigator.pushNamed(context, '/second')와 같이 경로명을 지정하여 수행할 수 있으며, 이를 통해 앱 내에서 화면 전환이 쉽게 이루어집니다.

728x90
반응형
728x90
반응형

dart http 타임아웃 응답 처리

http 요청에 대한 타임아웃 처리를 추가하려면 http 패키지의 timeout 메서드를 사용하여 요청이 일정 시간 내에 응답하지 않으면 자동으로 예외를 발생시키도록 할 수 있습니다. 또한, Dart에서 기본적으로 제공하는 예외 처리 방법을 사용하여 네트워크 오류, 응답 지연 등 다양한 상황에 대처할 수 있습니다.

다음은 타임아웃과 예외 처리 방법을 추가한 예제입니다.

1. GET 요청에 타임아웃 및 예외 처리 추가

import 'dart:convert';
import 'package:http/http.dart' as http;

void main() async {
  var url = Uri.parse('https://jsonplaceholder.typicode.com/posts/1');
  
  try {
    // GET 요청에 5초 타임아웃 추가
    var response = await http.get(url).timeout(Duration(seconds: 5));
    
    if (response.statusCode == 200) {
      // 성공적인 응답 처리
      var data = jsonDecode(response.body);
      print(data);
    } else {
      print('GET 요청 실패: ${response.statusCode}');
    }
  } on http.ClientException catch (e) {
    print('네트워크 오류 발생: $e');
  } on TimeoutException catch (_) {
    print('요청이 타임아웃되었습니다.');
  } catch (e) {
    print('예상치 못한 오류 발생: $e');
  }
}

2. PUT 요청에 타임아웃 및 예외 처리 추가

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'dart:async';

void main() async {
  var url = Uri.parse('https://jsonplaceholder.typicode.com/posts/1');
  
  var body = jsonEncode({
    'id': 1,
    'title': 'updated title',
    'body': 'updated body',
    'userId': 1,
  });

  try {
    // PUT 요청에 5초 타임아웃 추가
    var response = await http.put(
      url,
      headers: {'Content-Type': 'application/json; charset=UTF-8'},
      body: body,
    ).timeout(Duration(seconds: 5));

    if (response.statusCode == 200) {
      var data = jsonDecode(response.body);
      print(data);
    } else {
      print('PUT 요청 실패: ${response.statusCode}');
    }
  } on http.ClientException catch (e) {
    print('네트워크 오류 발생: $e');
  } on TimeoutException catch (_) {
    print('요청이 타임아웃되었습니다.');
  } catch (e) {
    print('예상치 못한 오류 발생: $e');
  }
}

주요 포인트:

  1. 타임아웃 처리: timeout(Duration(seconds: N)) 메서드를 사용하여 일정 시간이 지나도 응답이 없으면 TimeoutException이 발생합니다.
  2. 예외 처리: try-catch 블록을 사용하여 다양한 예외(ClientException, TimeoutException, 그 외 일반적인 예외) 상황에 대한 처리를 추가할 수 있습니다.
  3. 네트워크 오류 처리: 인터넷 연결 문제나 서버 접속 불가 등 다양한 네트워크 관련 예외를 http.ClientException으로 처리할 수 있습니다.

이 방법을 사용하면 요청이 응답하지 않거나 네트워크 오류가 발생했을 때 안전하게 대처할 수 있습니다.

728x90
반응형
728x90
반응형

기존 Flutter 앱에 데스크톱 지원 추가

  • 기존 Flutter 프로젝트에 데스크톱 지원을 추가하려면 루트 프로젝트 디렉토리에서 터미널에서 다음 명령을 실행합니다.
flutter create --platforms=windows,macos,linux .
  • 이렇게 하면 기존 Flutter 프로젝트에 필요한 데스크톱 파일과 디렉토리가 추가됩니다. 특정 데스크톱 플랫폼만 추가하려면 목록을 변경하여 추가하려는 플랫폼만 포함시킵니다.
728x90
반응형

'Flutter Dart' 카테고리의 다른 글

Flutter MaterialApp 네비게이션 및 routes  (0) 2024.10.10
dart http 타임아웃 응답 처리  (1) 2024.10.09
pubspec.yaml 파일의 name 필드 제한사항  (1) 2024.09.26
Dart await  (0) 2024.09.09
Dart final  (0) 2024.09.09

+ Recent posts