반응형

📘 var를 사용한 선언

변수는 var 변수명 = 값; 으로 선언합니다. 변수에 값이 들어가면 자동으로 타입을 추론하는 타입 추론 기능을 제공하므로 명시적으로 타입을 선언하지 않아도 됩니다.

void main (){
	
    var name = '주녘주녘';
    print(name);
    
    // 변수의 값 변경가능
    name = '주녘';
    print(name);
    
    // 변수명 중복은 불가
    // var name = '플러터의신'; <- 오류남

}

 

📘 dynamic을 사용한 변수선언

var 타입은 변수의 값을 사용해서 변수의 타입을 유추하는 키워드입니다. 타입을 한 번 유추하면 추론된 타입이 고정됩니다.

따라서 고정된 변수 타입과 다른 변수 타입의 값을 같은 변수에 다시 저장하면 에러가 발생합니다. 하지만 dynamic 키워드를 사용하면 변수의 타입이 고정되지 않아서 다른 타입의 값을 저장할 수 있습니다.

 

void main() {

	dynamic name = '주녘주녘';
	name = 1;

}

 

📘 final/const를 사용한 변수 선언

final과 const는 변수의 값을 처음 선언 후 변경할 수 없습니다.

void main () {

	final String name = '뉴진스';
 	name = '르세라핌';	// 에러발생 final로 선언한 변수는 선언 후 값을 변경 불가
    
    
  	const String name2 = '아이브';
 	name2 = '뉴진스';	// 에러발생 final로 선언한 변수는 선언 후 값을 변경 불가

}

 

- final은 런타임, const는 빌드타임 상수입니다.

- 코드를 실행하지 않은 상태에서 값이 확정되면 const를, 실행할 때 확정되면 final을 사용!

 

📘 컬렉션

여러 값을 하나의 변수에 저장할 수 있는 타입입니다.

여러 값을 순서대로 저장하거나(List), 특정 키값 기반으로 빠르게 값을 검색해야 하거나(Map), 중복된 데이터를 제거할 때(Set) 사용됩니다. 컬렉션 타입은 서로의 타입으로 자유롭게 형변환이 가능하다는 큰 장점이 있습니다.

 

🔷 List 타입

- 여러 값을 순서대로 한 변수에 저장할 때 사용

- 리스트의 구성 단위를 원소라고 합니다.

- 리스트명[인덱스] 형식으로 특정 원소에 접근 가능합니다.

- 인덱스는 원소의 순번

- 제일 첫 원소는 0으로 지정, 마지막 원소는 리스트 길이 -1 로 지정

void main() {

  List<String> iveList = ['유진', '원영', '이서', '레이', '리즈', '가을'];

  print('----------- 전체 리스트를 출력해보자 -----------');
  print(iveList);
  print('----------- 첫번째 원소를 출력해보자 -----------');
  print(iveList[0]);
  print('----------- 다섯번째 원소를 출력해보자 -----------');
  print(iveList[4]);
  print('----------- 리스트 길이를 출력해보자 -----------');
  print(iveList.length);

  print('----------- 인덱스 값 변경 -----------');
  iveList[3] = '주녘';

  print('----------- 전체 리스트를 출력해보자 -----------');
  print(iveList);
}
출력값



----------- 전체 리스트를 출력해보자 -----------
[유진, 원영, 이서, 레이, 리즈, 가을]
----------- 첫번째 원소를 출력해보자 -----------
유진
----------- 다섯번째 원소를 출력해보자 -----------
리즈
----------- 리스트 길이를 출력해보자 -----------
6
----------- 인덱스 값 변경 -----------
----------- 전체 리스트를 출력해보자 -----------
[유진, 원영, 이서, 주녘, 리즈, 가을]

 

- 리스트 길이는 length를 가져와 확인할 수 있습니다.

 

📘 add() 함수

add() 함수는 List에 값을 추가할 때 사용되며 추가하고 싶은 값을 매개변수에 입력하면 됩니다.

void main() {

  List<String> iveList = ['유진', '원영', '이서', '레이', '리즈', '가을'];

  iveList.add('주녘'); // 리스트의 끝에 추가됨

  print(iveList);
}
/Users/junhyuk/flutter/bin/cache/dart-sdk/bin/dart --enable-asserts /Users/junhyuk/Desktop/untitled/lib/main.dart
[유진, 원영, 이서, 레이, 리즈, 가을, 주녘]

Process finished with exit code 0

 

📘 where() 함수

where() 함수는 List에 있는 값들을 순서대로 순회하면서 특정 조건에 맞는 값만 필터링하는데 사용됩니다.

매개변수에 함수를 입력해야하며, 입력된 함수는 기존 값을 하나씩 매개변수로 입력받습니다. 각 값별로 true를 반환하면 값을 유지하고, false를 반환하면 값을 버립니다. 순회가 끝나면 유지된 값들을 기반으로 이터러블이 반환됩니다.

 

* 이터러블?

- List나 Set 등의 컬렉션 타입들이 상속받는 클래스

- List나 Set 같은 컬렉션이 공통으로 사용하는 기능을 정의해둔 클래스

- where() 이나 map() 등 순서가 있는 값을 반환할 때 사용

 

void main() {

  List<String> iveList = ['유진', '원영', '이서', '레이', '리즈', '가을'];

  final newList = iveList.where((name) => name == '유진' || name == '원영', );  // '유진' 또는 '원영' 만 유지

  print(newList);
  print(newList.toList()); // 이터러블을 리스트로 다시 반환할 때 toList 사용
}
/Users/junhyuk/flutter/bin/cache/dart-sdk/bin/dart --enable-asserts /Users/junhyuk/Desktop/untitled/lib/main.dart
(유진, 원영)
[유진, 원영]

Process finished with exit code 0

 

📘 map() 함수

map 함수는 List에 있는 값들을 순서대로 순회하면서 값을 변경할 수 있습니다.

매개변수에 함수를 입력해야 하며 입력된 함수는 기존 값을 하나씩 매개변수로 받습니다. 반환하는 값이 현재값을 대체하며 순회가 끝나면 이터러블이 반환됩니다.

void main() {

  List<String> iveList = ['유진', '원영', '이서', '레이', '리즈', '가을'];

  final newIveList = iveList.map((name) => '아이브 $name');  // 리스트의 모든 값 앞에 '아이브' 추가

  print(newIveList);

  print(newIveList.toList());
}
(아이브 유진, 아이브 원영, 아이브 이서, 아이브 레이, 아이브 리즈, 아이브 가을)
[아이브 유진, 아이브 원영, 아이브 이서, 아이브 레이, 아이브 리즈, 아이브 가을]

 

반응형

'Flutter' 카테고리의 다른 글

플러터 기본위젯  (0) 2024.03.30
typedef와 함수  (0) 2024.03.30
TodoList App 만들기  (0) 2024.03.21
Flutter MVVM 패턴 - 1  (0) 2024.03.19
dart 비동기 프로그래밍 - 3  (0) 2024.03.15
반응형
🚩TodoList App 만들기 목표
1. 폴더 구조 잡기
2. main 파일 만들어 보기
3. Model 클래스 생성해보기
4. View 만들어 보기
5. ViewModel 만들어 보기
6. view(todo_list_view.dart) 에 데이터 분리 하기

 

 

1. 프로젝트 구조 구성하기

2. main.dart 코드 작성하기

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: SafeArea(
        child: Scaffold(
          appBar: AppBar(title: const Text("TodoList"),),
          body: Center(
            child: Text("My Todo"),
          ),
        ),
      ),
    );
  }
}

 

import 'package:flutter/material.dart';
import 'package:my_todo_mvvm/views/todo_list_view.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: SafeArea(
        child: Scaffold(
          appBar: AppBar(title: const Text("TodoList"),),
          body: TodoListView(),
        ),
      ),
    );
  }
}

 

3. Model 클래스 생성해보기

/models/todo_item.dart 파일 생성하기

// Model
class TodoItem {
  String title;
  bool isDone;

  TodoItem({required this.title, this.isDone = false});
}

 

 

4. view 만들어 보기

/views/todo_list_view.dart 파일 생성하기 (1단계)

import 'package:flutter/material.dart';


// View 클래스 

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

  @override
  State<TodoListView> createState() => _TodoListViewState();
} // end of TodoListView class


class _TodoListViewState extends State<TodoListView> {

  final TextEditingController _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Column(
        children: [
          TextField(
            controller: _controller,
            decoration: InputDecoration(
              hintText: 'Enter todo item...',
              suffix: IconButton(
                icon: Icon(Icons.add),
                onPressed: () {
                 setState(() {
                   // build() 메서드 재 호출
                   _controller.clear();
                 });

                },
              )
            ),
          ),


        ],
      ),
    );
  }
} // end of _TodoListViewState

 

TextEditingController는 TextField 위젯에서 사용자 입력을 관리하는데 사용되는 클래스입니다. 이를 통해 TextField에서 사용자가 입력한 값에 접근하거나, TextField의 값을 변경하거나, TextField를 초기화하는 등 다양한 작업을 수행할 수 있습니다.

  1. 입력값 접근:
    • *TextEditingController*는 _controller.text 프로퍼티를 통해 현재 **TextField*에 입력된 값을 가져올 수 있습니다. 사용자가 **TextField*에 입력한 내용을 가져와서 로직에 활용하거나 다른 곳에 표시할 수 있습니다.
  2. 입력값 변경:
    • *_controller.text = 'newValue'*와 같이 **TextEditingController*의 text 프로퍼티를 통해 **TextField*의 값을 프로그래밍 방식으로 변경할 수 있습니다.
  3. 입력값 초기화:
    • *TextEditingController*의 clear 메서드를 사용하여 **TextField*의 내용을 쉽게 지울 수 있습니다. 이 기능은 사용자가 입력을 완료한 후 입력 필드를 초기화할 때 유용합니다.
  4. 입력 변경 감지:
    • *TextEditingController*에 리스너를 추가하여 **TextField*의 값이 변경될 때마다 알림을 받을 수 있습니다. 이를 통해 입력값이 변경될 때 특정 동작을 수행하도록 설정할 수 있습니다.

 

화면 만들기 2단계

import 'package:flutter/material.dart';
import 'package:my_todo_mvvm/models/todo_item.dart';

// View 클래스

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

  @override
  State<TodoListView> createState() => _TodoListViewState();
} // end of TodoListView class

class _TodoListViewState extends State<TodoListView> {
  final TextEditingController _controller = TextEditingController();

  // 샘플 데이터 만들어 보기
  List<TodoItem> _todoItems = [
    TodoItem(title: '플러터 공부하기', isDone: false),
    TodoItem(title: '낮잠 자기', isDone: true),
  ];

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Column(
        children: [
          TextField(
            controller: _controller,
            decoration: InputDecoration(
              hintText: 'Enter todo item...',
              suffix: IconButton(
                icon: Icon(Icons.add),
                onPressed: () {
                  setState(() {
                    // build() 메서드 재 호출
                    _controller.clear();
                  });
                },
              ),
            ),
          ),
          //
          Expanded(
            child: ListView.builder(
              itemCount: _todoItems.length,
              itemBuilder: (context, index) {
                var item = _todoItems[index];
                // 두개의 인수 값을 받아서 위젯을 리턴 시키면 된다.
                return ListTile(
                  title: Text(item.title),
                  trailing: Checkbox(
                    value: item.isDone,
                    onChanged: (value) {
                      print("value : ${value}");
                      setState(() {
                        _todoItems[index].isDone = value ?? false;
                      });
                    },
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
} // end of _TodoListViewState

 

ViewModel 만들기

 

// ViewModel
import 'package:my_todo_mvvm/models/todo_item.dart';

class TodoListViewModel {

  // 화면 사용될 데이터
  List<TodoItem> _items = []; // private

  // get 메서드 만들어 주기
  List<TodoItem> get items => _items;

  // 리스트에 TodoItem 객체를 추가하는 메서드 만들기
  void addItem(String title) {
    _items.add(TodoItem(title: title, isDone: false));
  }

  void toggleItem(TodoItem todo) {
    todo.isDone = !todo.isDone;
  }

}

 

todo_list_view.dart 수정

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:my_todo_mvvm/models/todo_item.dart';
import 'package:my_todo_mvvm/view_models/todo_list_view_model.dart';

// View 클래스

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

  @override
  State<TodoListView> createState() => _TodoListViewState();
} // end of class

class _TodoListViewState extends State<TodoListView> {

  final TextEditingController _controller = TextEditingController();
  final TodoListViewModel listViewModel = TodoListViewModel();





  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Column(
        children: [
          TextField(
            controller: _controller,
            decoration: InputDecoration(
              hintText: 'Enter Todo Item',
              suffix: IconButton(
                icon: Icon(Icons.add),
                onPressed: (){
                  setState(() {
                    listViewModel.addItem(_controller.text);
                    _controller.clear();
                  });
                },
              )
            ),
          ),
          //
          Expanded(
            child: ListView.builder(
              itemCount: listViewModel.items.length,
              itemBuilder: (context, index){
                var item = listViewModel.items[index];
                // 두개 인수값을 받아서 위젯을 리턴
                return ListTile(
                  title: Text(item.title),
                  trailing: Checkbox(
                    value: item.isDone,
                    onChanged: (value) {
                      print('value : ${value}');
                      setState(() {
                        listViewModel.toggleItem(item);
                      });
                    },
                  ),
                );
            },),
          )

        ],
      ),
    );
  }
} // end of _TodoListViewState

 

 

반응형

'Flutter' 카테고리의 다른 글

typedef와 함수  (0) 2024.03.30
dart 이론  (1) 2024.03.30
Flutter MVVM 패턴 - 1  (0) 2024.03.19
dart 비동기 프로그래밍 - 3  (0) 2024.03.15
dart 비동기 프로그래밍 - 2  (0) 2024.03.15
반응형

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 이론  (1) 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
반응형
1. JSON 응답 타입을 확인 하자
2. JSON Object Type 의 이해 (동기적 방식 처리)
3. JSON Array Type 의 이해 (동기적 방식 처리)

 

 

1. JSON 응답 타입을 확인 하자 - Resposne → body(String)

// Json Object type -> { "key" : "value"}
// Json Array type -> [{},{},{} ]

 

 

Todo DTO

// TODO : DTO 개념 클래스를 설계할 때 nullable 타입으로 설계하자

class Todo {
  int? userId;
  int? id;
  String? title;
  bool? completed;

  // 기본 생성자
  // 강제성 - 생성자
  Todo(this.userId, this.id, this.title, this.completed);

  // 명명된 생성자2 - Map을 넣으면 Todo 오브젝트가 반환되는 코드를 작성
  // 이니셜 라이져 (변수를 초기화 해주는 문법)
  Todo.fromJson(Map<String, dynamic> json)
      : userId = json['userId'],
        id = json['id'],
        title = json['title'],
        completed = json['completed'];

  @override
  String toString() {
    return '내가 보는 - Todo{userId: $userId, id: $id, title: $title, completed: $completed}';
  }
}

 

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

void main() async {
  var res = await fetchTodo();

  if (res.statusCode == 200) {
    print("통신 성공");
    Map<String, dynamic> result = json.decode(res.body);
    var todo1 = Todo.fromJson(result);

    print("title : ${todo1.title} ");
    print("completed : ${todo1.completed} ");
  } else {
    print("통신 실패");
  }
}

// 통신을 하는 함수 만들어 보기
Future<http.Response> fetchTodo() async {
  const url = 'https://jsonplaceholder.typicode.com/todos/1';

  final response = await http.get(Uri.parse(url));

  return response;
}

 

실행결과

통신 성공
title : delectus aut autem 
completed : false 

Process finished with exit code 0

 

 

이번에는 user 정보를 파싱해서 출력해보자.

 

 

User.dart

// TODO : DTO 개념 클래스를 설계할 때 nullable 타입으로 설계하자

import 'address.dart';
import 'company.dart';

class User {
  int? id;
  String? name;
  String? username;
  String? email;
  Address? address;
  String? phone;
  String? website;
  Company? company;

  User(this.id, this.name, this.username, this.email, this.address, this.phone,
      this.website, this.company);

  User.fromJson(Map<String, dynamic> json)
      : id = json['id'],
        name = json['name'],
        username = json['username'],
        email = json['email'],
        address = json['address'] != null ? Address.fromJson(json['address']) : null,
        phone = json['phone'],
        website = json['website'],
        company = json['company'] != null ? Company.fromJson(json['company']) : null;

  @override
  String toString() {
    return 'User{id: $id, name: $name, username: $username, email: $email, address: $address, phone: $phone, website: $website, company: $company}';
  }
}

 

 

address.dart

import 'geo.dart';

class Address {
  String? street;
  String? suite;
  String? city;
  String? zipcode;
  Geo? geo;

  Address({this.street, this.suite, this.city, this.zipcode, this.geo});

  Address.fromJson(Map<String, dynamic> json)
      : street = json['street'],
        suite = json['suite'],
        city = json['city'],
        zipcode = json['zipcode'],
        geo = json['geo'] != null ? Geo.fromJson(json['geo']) : null;

  @override
  String toString() {
    return 'Address{street: $street, suite: $suite, city: $city, zipcode: $zipcode, geo: $geo}';
  }
}

 

geo.dart

class Geo {
  String? lat;
  String? lng;

  Geo({this.lat, this.lng});

  Geo.fromJson(Map<String, dynamic> json)
      : lat = json['lat'],
        lng = json['lng'];

  @override
  String toString() {
    return 'Geo{lat: $lat, lng: $lng}';
  }
}

 

 

main.dart

import 'dart:convert';

import 'package:dart_future_v1/user.dart';
import 'package:http/http.dart' as http;

void main() async {


  var res = await fetchUser();

  if(res.statusCode == 200) {
    print("통신 성공");
    Map<String, dynamic> result = json.decode(res.body);
    var user1 = User.fromJson(result);

    print("id : ${user1.id} ");
    print("name : ${user1.name} ");
    print("username : ${user1.username} ");
    print("email : ${user1.email} ");
    print("address : ${user1.address} ");
    print("phone : ${user1.phone} ");
    print("website : ${user1.website} ");
    print("company : ${user1.company} ");


  } else {
    print("통신 실패");
  }





}


// 통신을 하는 함수 만들어 보기
Future<http.Response> fetchUser() async {
  const url = 'https://jsonplaceholder.typicode.com/users/1';

  final response = await http.get(Uri.parse(url));

  return response;
}

 

 

실행결과


C:/devtool/flutter/bin/cache/dart-sdk/bin/dart.exe --enable-asserts C:\devtool\class_flutter\dart_future_v1\lib\main11.dart
통신 성공
id : 1 
name : Leanne Graham 
username : Bret 
email : Sincere@april.biz 
address : Address{street: Kulas Light, suite: Apt. 556, city: Gwenborough, zipcode: 92998-3874, geo: Geo{lat: -37.3159, lng: 81.1496}} 
phone : 1-770-736-8031 x56442 
website : hildegard.org 
company : Company{name: Romaguera-Crona, catchPhrase: Multi-layered client-server neural-net, bs: harness real-time e-markets} 

Process finished with exit code 0
반응형

'Flutter' 카테고리의 다른 글

TodoList App 만들기  (0) 2024.03.21
Flutter MVVM 패턴 - 1  (0) 2024.03.19
dart 비동기 프로그래밍 - 2  (0) 2024.03.15
dart 비동기 프로그래밍 -1  (0) 2024.03.14
블로그 웹 앱 만들어보기  (0) 2023.12.15
반응형

JSON 형식에 문자열 데이터를 dart 코드로 변환 처리

JSON 문자열을 Dart 객체로 변환하는 과정을 "JSON 파싱"이라고 합니다.
JSON 파싱은 일반적으로 JSON 형식의 문자열 데이터를 특정 프로그래밍 언어의 데이터 구조나 객체로 변환하는 작업을 의미합니다.

 

Dart 객체로 변환하기 위해 우리는 dart:convert 라이브러리의 json.decode() 메서드를 사용

 

import 'dart:convert';

void main() {

  // 1단계 - 통신을 x 직접 json 형식의 데이터를 만들어 보자
  String jsonStr = '''
        {
          "userId": 1,
          "id": 100,
          "title": "json 파싱이란?",
          "completed": false   
        }
   ''';
  // 위 코드는 단지 형식이 있는 문자열입니다 (json)

  // 1단계 - 라이브러리가 필요 하다.
  // JSON 문자열을 파싱하여 먼저 Map 객체로 변환을 해야 한다.
  Map<String, dynamic> jsonStrToMap = json.decode(jsonStr); // Map 구조로 변환 해줌
  print(jsonStrToMap.runtimeType);
  print(jsonStrToMap);
  // Map --> 반복문 활용을 많이 한다.
  jsonStrToMap.forEach((key, value) {
    print("key - ${key}" );
    print("key - ${value}" );
    print("---------------------");
  });
}

class Todo {

  int userId;
  int id;
  String title;
  bool completed;

  // 기본 생성자 1
  // 강제성 - 생성자
  Todo(this.userId, this.id, this.title, this.completed);

  // 명명된 생성자 2 - Map를 넣으면 Todo 오브젝트가 반환 되는 코드를 작성
  // 이니셜 라이져 (변수를 초기화 해주는 문법)
  Todo.fromJson(Map<String, dynamic> json)
    : userId = json["userIkd"],
      id = json["id"],
      title = json["title"],
      completed = json["completed"];

  @override
  String toString() {
    return '내가 보는 - Todo{userId: $userId, id: $id, title: $title, completed: $completed}';
  }
}

 

실행결과

C:/devtool/flutter/bin/cache/dart-sdk/bin/dart.exe --enable-asserts C:\devtool\class_flutter\dart_future_v1\lib\main8.dart
_Map<String, dynamic>
{userId: 1, id: 100, title: json 파싱이란?, completed: false}
key - userId
value - 1
-------------------
key - id
value - 100
-------------------
key - title
value - json 파싱이란?
-------------------
key - completed
value - false
-------------------
내가 보는 - Todo{userId: 1, id: 100, title: json 파싱이란?, completed: false}
json 파싱이란?

Process finished with exit code 0
반응형

'Flutter' 카테고리의 다른 글

TodoList App 만들기  (0) 2024.03.21
Flutter MVVM 패턴 - 1  (0) 2024.03.19
dart 비동기 프로그래밍 - 3  (0) 2024.03.15
dart 비동기 프로그래밍 -1  (0) 2024.03.14
블로그 웹 앱 만들어보기  (0) 2023.12.15
반응형
동기성 - 모든 코드가 순차적으로 진행되는 형태
비동기 - 코드가 동시다발적으로 실행, 순착적으로 보장을 할 수 없는 형태

 

다트 언어에서 제공하는 Future, Stream, await, async는 모두 비동기 프로그래밍을 지원하는 기능입니다.
비동기 프로그래밍이란 시간이 오래걸리는 작업을 실행한 후 끝날 때까지 기다리지 않고 다음 작업을 실행하는 것입니다. 비동기 프로그래밍과 반대되는 개념은 동기 프로그래밍으로 어떤 작업을 실행하고 끝날 때까지 기다렸다가 그 다음 작업을 수행하는 것입니다.
dart 비동기 프로그래밍은 프로그램의 흐름을 중단시키지 않고, 무언가의 완료를 기다리는 동안 다른 작업을 수행할 수 있게 해줍니다. Dart에서는 이를 위해 Future와 Stream이라는 두 가지 주요 개념을 제공합니다.

 

동기로 실행되는 예를 들어 비동기 프로그래밍이 왜 필요한지 알아보겠습니다.

다음 코드는 버튼을 클릭하면 onPress() 함수가 호출되고, onPress() 함수에서 sum() 함수를 호출합니다.

여기서 sum() 함수에서 간단한 더하기를 처리했지만, 시간이 오래 걸리는 작업이라 가정하겠습니다.

void main() {

  onPress();

}

void sum() {
  var sum = 0;
  Stopwatch stopwatch = new Stopwatch();
  stopwatch.start();
  for(int i = 0; i < 500000000; i++) {
    sum += i;
  }
  stopwatch.stop();
  print("${stopwatch.elapsed}, result : $sum");
}

void onPress() {
  print('onPress top...');
  sum();
  print('onPress bottom...');
}

 

[실행결과]

onPress top...
0:00:00.161466, result : 124999999750000000
onPress bottom...

 

실행결과를 보면 onPress top이 출력되고 sum() 함수의 결과가 출력된 후에 onPress bottom 이 출력되었습니다.

즉, sum() 함수가 끝나야 onPress() 함수에서 sum() 함수를 호출한 다음 그 다음 줄이 실행됩니다. 결국, onPress() 함수는 sum() 함수의 실행이 끝날 때 까지 대기합니다.

만약 sum() 함수의 작업이 오래 걸린다면 문제가 됩니다. 왜냐하면 sum() 함수의 작업을 마칠때까지 onPress()의 나머지를 수행할 수 없기 때문입니다.

이처럼 시간이 오래걸리는 작업은 다양한데 네트워킹 또는 파일을 읽거나 쓰는 겨웅가 대표적입니다. 이런 작업을 동기로 프로그래밍하면 그 작업이 끝날 때 까지 사용자 이벤트나 화면을 처리할 수 없습니다. 따라서 앱의 성능이 떨어지는 문제가 있습니다.

// 키워드 묶음 // async / await / Future : 1회만 응답을 돌려 받는 경우
// async* / yield / Stream : 지속적으로 응답을 돌려 받아야 하는 경우

 

시나리오 코드 1 - Future 타입 사용해보기

import 'package:flutter/material.dart';

void main() {


// 비동기 프로그래밍
// 키워드 묶음 - async, await : Future (1회성 응답을 돌려 받는 경우)

print("task ...... 1");
// 비동기 함수 만들기
  var data1 = fetchData();
print("task ...... $data1");
print("task ...... 3");


}

// 비동기 함수 - Future
Future<String> fetchData() {
  // 2초 뒤에 데이터를 가져 옴
  return Future.delayed(Duration(seconds: 2), () {
    return "2초 data";
  });
}

 

Performing hot restart...
Syncing files to device sdk gphone x86...
Restarted application in 761ms.
I/flutter (28571): task ...... 1
I/flutter (28571): task ...... Instance of 'Future<String>'
I/flutter (28571): task ...... 3

 

시나리오 코드 2 - await 사용 (마치 동기 프로그래밍 처럼 활용할 수 있다)

규칙

await 키워드를 사용하는 포함하고 있는 함수는 반드시 함수 선언부에 async 키워드를 선언해주어야 한다.
import 'package:flutter/material.dart';

void main() async {


// 비동기 프로그래밍
// 키워드 묶음 - async, await : Future (1회성 응답을 돌려 받는 경우)

print("task ...... 1");
// 비동기 함수 만들기
  var data1 = await fetchData();
print("task ...... $data1");
print("task ...... 3");


}

// 비동기 함수 - Future
Future<String> fetchData() {
  // 2초 뒤에 데이터를 가져 옴
  return Future.delayed(Duration(seconds: 2), () {
    return "연산완료 처리";
  });
}

 

Performing hot restart...
Syncing files to device sdk gphone x86...
Restarted application in 783ms.
I/flutter (28571): task ...... 1
I/flutter (28571): task ...... 연산완료 처리
I/flutter (28571): task ...... 3

 

이해도를 높이기 위한 코드 작성

import 'package:flutter/material.dart';

void main()  {
  // Future 타입과 값을 꺼내는 방법
  // 비동기 함수를 - 동기성으로 변경하면 값이 자동으로 꺼내졌다.
  // Future 타입 선언
  Future<String> name = Future.value("주녘");
  Future<int> number = Future.value(29);
  Future<bool> isTrue = Future.value(true);

  print(name);
  print(number);
  print(isTrue);
  print("-------------------");
}

 

Performing hot restart...
Syncing files to device sdk gphone x86...
Restarted application in 1,177ms.
I/flutter (29095): Instance of 'Future<String>'
I/flutter (29095): Instance of 'Future<int>'
I/flutter (29095): Instance of 'Future<bool>'
I/flutter (29095): -------------------

 

 

async, await 사용하면?

import 'package:flutter/material.dart';

void main() async {
  // Future 타입과 값을 꺼내는 방법
  // 비동기 함수를 - 동기성으로 변경하면 값이 자동으로 꺼내졌다.
  // Future 타입 선언
  Future<String> name = Future.value("주녘");
  Future<int> number = Future.value(29);
  Future<bool> isTrue = Future.value(true);

  print(name);
  print(number);
  print(isTrue);
  print("-------------------");

  print(await name);
  print(await number);
  print(await isTrue);
  print("-------------------");
}

 

Performing hot restart...
Syncing files to device sdk gphone x86...
Restarted application in 807ms.
I/flutter (29095): Instance of 'Future<String>'
I/flutter (29095): Instance of 'Future<int>'
I/flutter (29095): Instance of 'Future<bool>'
I/flutter (29095): -------------------
I/flutter (29095): 주녘
I/flutter (29095): 29
I/flutter (29095): true
I/flutter (29095): -------------------

 

시나리오 코드 4 - Future 타입과 값 꺼내는 방법 - 2 (Future.then() 사용)

import 'package:flutter/material.dart';

void main()  {
  // Future 타입과 값을 꺼내는 방법
  // 비동기 함수를 - 동기성으로 변경하면 값이 자동으로 꺼내졌다.
  // Future 타입 선언
  Future<String> name = Future.value("주녘");
  Future<int> number = Future.value(29);
  Future<bool> isTrue = Future.value(true);

  print(name);
  print(number);
  print(isTrue);
  print("-------------------");

  // 1번 방법은 동기성으로 만들기
  // 2번 미래 타입을 소화시키는 2번째 방법 : 콜백 메서드의 활용
  // print(await name);
  name.then((value) => print("미래 타입 값 꺼내기 콜백 :  $value"));
  number.then((e) => print("xxxxx :  $e"));
  isTrue.then((value) { print("ooooo : $value"); });
}

 

Performing hot restart...
Syncing files to device sdk gphone x86...
Restarted application in 904ms.
I/flutter (29303): Instance of 'Future<String>'
I/flutter (29303): Instance of 'Future<int>'
I/flutter (29303): Instance of 'Future<bool>'
I/flutter (29303): -------------------
I/flutter (29303): 미래 타입 값 꺼내기 콜백 :  주녘
I/flutter (29303): xxxxx :  29
I/flutter (29303): ooooo : true

 

🎈 Key Point Future 타입을 소화 시키는 방법
1. 동기적 프로그래밍으로 만들어 준다.
2. 비동기 처리를 한다 - 콜백 메서드로 활용 한다.
3. 하나의 비동기 작업을 처리하기 위해 두 가지 방법을 혼합하여 사용할 수도 있다.

 

응용

import 'package:flutter/material.dart';

void main()  {


  // var result1 = addNumber1(100, 200);
  // print(result1);
  // print(result1.runtimeType);
  // result1.then((value) => print("result1 소화 : $value"));
  print('---------------------');
  var result2 = addNumber2(100, 200);
  result2.then((value) => print("result2 소화 : $value"));
  print('---------------------2222222222');
}

Future<int> addNumber2(int n1, int n2){

  return Future.delayed(Duration(seconds: 3), () => n1 + n2);

}

// 응용
// 함수 설계, 인수 2개 int 값을 받아서 2초 뒤에 연산되는 함수를 설계해보자.
// 동기적 방식으로 처리
Future<int> addNumber1(int n1, int n2) async {
  print("함수 시작 1");
  var result = 0;
  await Future.delayed(Duration(seconds: 2), () {
    result = n1 + n2;
  });
  print("함수 완료 2");
  return result;
}

 

import 'package:flutter/material.dart';

void main() async {


  // var result1 = addNumber1(100, 200);
  // print(result1);
  // print(result1.runtimeType);
  // result1.then((value) => print("result1 소화 : $value"));
  print('---------------------');
  var result2 = await addNumber2(100, 200);
  // result2.then((value) => print("result2 소화 : $value")); 동기방식으로 변경하기
  // 동기방식으로 변경하기
  print(result2);
  print('---------------------2222222222');
}

Future<int> addNumber2(int n1, int n2) {

  return Future.delayed(Duration(seconds: 3), () => n1 + n2);

}

// 응용
// 함수 설계, 인수 2개 int 값을 받아서 2초 뒤에 연산되는 함수를 설계해보자.
// 동기적 방식으로 처리
Future<int> addNumber1(int n1, int n2) async {
  print("함수 시작 1");
  var result = 0;
  await Future.delayed(Duration(seconds: 2), () {
    result = n1 + n2;
  });
  print("함수 완료 2");
  return result;
}
반응형

'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
블로그 웹 앱 만들어보기  (0) 2023.12.15
반응형


프로젝트 명 : 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