728x90

반응형 상태관리는  GetxController 를 상속받지 않아도 된다.

대신 하나 하나의 변수에 obs를 붙여서 listening을 한다. 메소드 내에 update()도 필요 없다.

 

GetX 반응형 상태관리 예제

import 'package:get/get.dart';
 
class CountController extends GetxController {
  RxInt counter = 0.obs;
  // 반응형은 obs 를 붙이고 int 형에서 RxInt 형으로 변경
  // RxDouble, RxString, Rx<Class명>, RxList<String>, Rx<ENUM명>
 
  void increment() {
    counter++;
    // update(); 는 필요 없다.
  }
 
  void decrement() {
    counter--;
  }
 
  void putNumber(int val){
    counter(val);
  }
 
  @override
  void onInit() {
    //ever(counter, (_) => print('매번 호출'));
    //once(counter, (_) => print('한번만 호출'));
    //debounce(counter, (_) => print('마지막 변경에 한번만 호출'), time: Duration(seconds: 1) );
    super.onInit();
  }
}
 

 

 

MaterialApp → GetMaterialApp 으로 변경한다.

실제 사용하는 곳에서 인스턴스를 생성해야 한다.  Get.put(CountController());

단순 상태관리는 GetBuilder를 사용했다면, 반응형은 Obx 와 GetX 두 가지 방법이 있다. 

비교적 사용법이 간단한 Obx 를 많이 사용한다.

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'controller.dart';
 
void main() {
  runApp(const MyApp());
}
 
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
 
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}
 
class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;
 
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}
 
class _MyHomePageState extends State<MyHomePage> {
 
  @override
  Widget build(BuildContext context) {
    Get.put(CountController());
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            const Text(
              'You have pushed the button and count :',
            ),
            Obx(() {
              //print('Update UI'); // 화면 렌더링을 확인하고 싶을 때
              return Text(
                '${Get.find<CountController>().counter.value}',
                style: Theme.of(context).textTheme.headline4,
              );
            } ),
          ],
        ),
      ),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
              onPressed: () {
                Get.find<CountController>().increment();
              },
              backgroundColor: Colors.green,
              child: const Icon(Icons.add)),
          SizedBox(width: 10, height: 10,), // 여백을 만들기 위해서 넣음.
          FloatingActionButton(
              onPressed: () {
                Get.find<CountController>().decrement();
              },
              backgroundColor: Colors.pink,
              child: const Icon(Icons.remove)),
          SizedBox(width: 10, height: 10,), // 여백을 만들기 위해서 넣음.
          FloatingActionButton(
              onPressed: () {
                Get.find<CountController>().putNumber(5);
              },
              backgroundColor: Colors.blue,
              child: const Text('5')),
        ],
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

Obx 는 화살표 함수(람다식 표현)로 수정할 수 있으나, 로그 확인을 위해서 print 함수를 찍어서 알아보기 위함이다.

 

 

Rx 타입 자료형

class User{
  String name;
  int age;
  User({this.name, this.age})
}

enum NUM {FIRST,SECOND}

class CountReactiveGetx extends GetxController{
  RxInt count = 0.obs;
  RxDouble _double = 0.0.obs;
  RxString value = "".obs;
  Rx<NUM> nums = NUM.FIRST.obs;
  Rx<User> user = User(name:'Json',age:10).obs;
  RxList<String> list=[].obs;
  
  void increase(){
    count++;
    _double++;
    _double(424);
    
    nums(NUM.SECOND);
    user(User());
    user.update((_user){ _user.name='James'});
    
    //list.addAll();
    //list.add();
    //list.addIf(user.value.name == 'Json','OKAY') //(조건,추가할 요소)
  } 
  
}

출처 : https://www.youtube.com/watch?v=TjC1ka8fZJw 유투브 영상

블로그 이미지

Link2Me

,
728x90

요즈음은 비동기 프로그래밍이 기본이다.

다트에서 비동기에 관련된 핵심 클래스는 Future 와 Stream 이며, async 와 await 키워드와 함께 사용한다.

다트 코드는 단일 쓰레드에서 실행된다.

 

Future 클래스

비동기 함수는 Future 클래스를 리턴한다. 이 Future 클래스는 미래에 비동기 작업이 끝나면 값을 받아오는 클래스이다
Future클래스에는 콜백을 2개 등록할 수 있다.
 - 비동기 함수가 성공시 성공에 대한 콜백
 - 비동기 함수가 error가 발생 시 exception을 일으키는 콜백

Future는 두가지의 state를 가진다.
 - Uncompleted, 비동기 함수가 아직 끝나지 않은 상태
 - Completed, 비동기 함수가 작업이 끝난 상태. 성공하면 value를, 실패하면 error 값을 가져온다

Future<T>는 비동기 처리가 성공시 T타입의 값을 가져온다. 만약 T가 String이면 Future<String> 으로 적어준다.
값을 리턴하지 않을 경우 Future<void> 라고 적는다.

void main(){
  print('Before the Future');
  Future((){
    print('Running the Future');
  }).then((_) => print('Future is complete.') );
  print('After the Future');
 
  /*
  1. 다트에 의해서 future 객체가 내부적인 배열에 등록
  2. Future 관련해서 실행되어야 하는 코드들이 이벤트 Queue 에 등록
  3. 불완전한 future 객체 반환
  4. Synchronous 방식으로 실행되어야 할 코드 먼저 실행
  5. 최종적으로 실제적인 data 값이 future 객체로 전달
  */
 
  /* 실행 결과
  Before the Future
  After the Future
  Running the Future
  Future is complete.
   */
}

Future의 then(), catchError() 함수

then() 함수를 통해 Future가 complete 되면 실행해줄 콜백을 등록할 수 있다.
또한 catchError() 함수를 통해 에러처리를 해줄 수 있다.

 

 
String createOrderMessage(){
  var order = fetchUserOrder();
  return 'Your order is : ${order}';
}
 
Future<String> fetchUserOrder(){
  return Future.delayed(
    Duration(seconds: 2),
      () => 'Large Latte',
  );
}
 
void main(){
  print('Fetching user order...');
  print(createOrderMessage());
}
 
/* 실행결과
Fetching user order...
Your order is : Instance of 'Future<String>'
*/

원하지 않은 결과가 출력되었다.

 

 

async : async 함수 본문 앞에 키워드를 사용하여 비동기로 표시할 수 있다.
async function : async 함수는 async 키워드로 표시된 함수다.
await : await 키워드를 사용하여 비동기식의 완성된 결과를 얻을 수 있다.

await 키워드는 단지 async 내에서 작동하는 기능이다.

 

Async method

- 메소드를 통해 나오는 결과물은 Future

- await 키워드를 만날 때까지 synchronous 방식으로 코드 처리

- await 키워드를 만나면 future 가 완료될 때까지 대기

- future 가 완료되자마자 그 다음 코드들을 실행

 

코드를 아래와 같이 수정한다.

future가 완료될 때까지 일시 중단하려면 async 함수에서 await를 사용하면 된다.

Future<String> createOrderMessage() async {
  print('synchronous');
  var order = await fetchUserOrder();
  return 'Your order is : ${order}';
}
 
Future<String> fetchUserOrder(){
  return Future.delayed(
    Duration(seconds: 2),
      () => 'Large Latte',
  );
}
 
void main() async {
  print('Fetching user order...');
  print(await createOrderMessage());
}
 
/* 실행 결과
Fetching user order...
synchronous
Your order is : Large Latte
*/

이제 원하는 결과가 출력되었다.

 

 

void main() async {
  methodA();
  await methodB();
  await methodC("main");
  methodD();
}
 
void methodA() {
  print('A method run.');
}
 
methodB() async {
  print('B method start.');
  await methodC('B');
  print('B method end.');
}
 
methodC(String s) {
  print('C method start FROM ${s}');
  Future(() => print('C Future running FROM ${s}'))
      .then((_) => print('C Future is complete FROM ${s}'));
  print('C method end FROM ${s}');
}
 
void methodD() {
  print('D method run.');
}
 
/* 실행결과
A method run.
B method start.
C method start FROM B
C method end FROM B
B method end.
C method start FROM main
C method end FROM main
D method run.
C Future running FROM B
C Future is complete FROM B
C Future running FROM main
C Future is complete FROM main
*/

 

'Flutter 앱 > Dart 언어' 카테고리의 다른 글

Dart Collection  (0) 2022.06.28
Dart Class(클래스)  (0) 2022.06.27
Dart function(함수)  (0) 2022.06.22
Dart Type 검사(is, is!)  (0) 2022.06.22
Dart const, final  (0) 2022.06.22
블로그 이미지

Link2Me

,
728x90

https://pub.dev/packages/get/install  에 접속한다.

설치 안내에 따라 dependencies에 get 을 추가한다.

 

터미널창을 열고 아래 명령어를 입력하고 엔터키를 친다.

flutter pub add get

 

 

단순 상태관리 예제

본문에 포함된 주석을 참조하면 이해하는데 도움된다.

import 'package:get/get.dart';
 
class CountController extends GetxController {
  // Flutter에서 상태 관리에 가장 많이 사용되는 패키지인 GetX
  // 상태 관리 외에도 route 관리, 다국어 지원, 화면크기 가져오기, API 호출 기능
 
  int _counter = 0// _를 붙이면 private 변수
  int get counter => _counter;
 
  void increment() {
    _counter++;
    update();
  }
 
  void decrement() {
    _counter--;
    update();
  }
}

 

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'controller.dart';
 
void main() {
  runApp(const MyApp());
}
 
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
 
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      // MaterialApp 대신에 GetMaterialApp
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}
 
class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;
 
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}
 
class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    CountController controller = Get.put(CountController());
 
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            const Text(
              'You have pushed the button this many times:',
            ),
            // 상태의 변화를 감지하고 변경된 상태값을 적용하기 위해서는
            // GetBuilder를 사용해야 한다.
            GetBuilder<CountController>(builder: (_) {
              return Text(
                '${controller.counter}',
                style: Theme.of(context).textTheme.headline4,
              );
            }),
          ],
        ),
      ),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
              onPressed: () {
                controller.increment();
              },
              backgroundColor: Colors.green,
              child: const Icon(Icons.add)),
          SizedBox(width: 10, ), // 여백을 만들기 위해서 넣음.
          FloatingActionButton(
              onPressed: () {
                controller.decrement();
              },
              backgroundColor: Colors.pink,
              child: const Icon(Icons.remove)),
        ],
      ),
      // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

 

CountController controller = Get.put(CountController());
controller.counter;
controller.increment();
controller.decrement();
 
// 위의 코드 대신에 아래 코드로 대체 사용할 수 있다.
Get.find<CountController>().counter;
Get.find<CountController>().increment();
Get.find<CountController>().decrement();

 

 

 

블로그 이미지

Link2Me

,
728x90

본 게시글 작성 시점은 Null safety 가 적용된 이후 시점이라 Named Optional Parameters 부분이 다른 블로그/구글자료와   다를 수 있다.

  • Edit the package’s pubspec.yaml file, setting the minimum SDK constraint to 2.12.0:
environment:
  sdk: '>=2.12.0 <3.0.0'

 

 

Dart는 객체 지향 언어이다. Dart에서는 모든 것이 객체이기 때문에 함수도 객체이다.

함수가 객체이기 때문에 변수가 함수를 참조할 수 있다. 함수의 인자로 함수를 전달할 수 있다.

 

int add(int a, int b) {
  return a + b;
}
 
void main() {
  int x = 4;
  int y = 5;
 
var z = add(x, y); // 객체이기 때문에 함수를 참조할 수 있다.
 
  print("Output: $z");
}

 

 

int add(int a, int b) {
  return a + b;
}
 
// return_type func_name(parameters) => expression;
int sub(int a, int b) => a - b; // 람다식 표현
 
// Lambda functions are also called Arrow functions.
int mul(int a, int b) => a * b ; 
 
void main() {
  int x = 10;
  int y = 5;
 
  var z = add(x, y); // 객체이기 때문에 함수를 참조할 수 있다.
  print("Output: $z"); // Output: 15
 
  print('${x + y} X ${x - y} = ${mul(add(x,y), sub(x,y))}');
  // 15 X 5 = 75
}

변수 앞에 $ 기호를 붙여 문자열 내에 변수를 삽입할 수 있다.

또한 $ 기호 뒤에 {}로 둘러싸 수식을 포함한 각종 표현식을 사용할 수 있다.

함수의 인자로 함수를 전달한 것을 확인할 수 있다.

 

 

선택 매개변수

함수 호출 시 매개변수명을 이용하여 인자 값을 넘겨줄 수 있다. 매개변수명으로 인자 값을 넘겨줄 때 {매개변수명: value}로 해줘야 한다.

// Ordered Optional Parameters
void printSomething(int a, int b, [int c = 99]){
  // 필수 매개변수와 선택 매개변수를 함께 사용하고 싶다면
  // 필수 매개변수를 앞에 둔다.
  print(a + b + c);
}
 
// Named Optional Parameters
void namedSomething(int a, int b, {int c = 99}){
  print(a + b + c);
}
 
// Named Optional Parameters
void person({required String name, int age = 0}){
  print('${name}, ${age}');
}
 
class Person {
  String name;
  int age;
 
  Person({required this.name, required this.age });
}
 
main(){
  printSomething(2,3);
  printSomething(2,3,5);
 
  namedSomething(2,3);
  namedSomething(2,3,c : 10);
 
  person(name: '홍길동', age: 25); // OK
  person(name: '홍길동'); // OK
  // person(); // error
 
  // Class 에 객체 할당
  Person p = Person(name: '홍길동', age: 30);
  print('${p.name}, ${p.age}');
}

Java 는 선택 인자를 제공하지 않는다. 따라서 선택 인자를 구현하기 위해서는 오버로딩으로 별도로 2개 함수를 작성해야  한다.

이름 있는 인자는 Python 에서도 제공하는데 필수 인자와 선택 인자 모두 이름을 넣을 수 있는 반면,

다트에서는 선택 인자에만 이름을 넣을 수 있다.

 

 

null safety 가 적용되어 Named Optional Prameters를 사용하고자 한다면

String 지시어에 ? 가 없으면 반드시 required 를 붙이도록 하고 있다.

// Named Optional Parameters
void person({required String name, int age = 0}){
  print('${name}, ${age}');
}
 
void person1({String? name, int? age}){
  print('${name}, ${age}');
}
 
void person2(String name, {int? age}){
  // 다트에서는 모든 것이 객체이기 때문에
  // 초기값이 설정되지 않는 것은 0 이 아니라 null 이다.
  print('${name}, ${age}');
}
 
main(){
  person(name: '홍길동', age: 25); // 홍길동, 25
  person(name: '홍길동'); // 홍길동, 0
  // person(); // error
 
  person1(name: '홍길동', age: 30); // 홍길동, 30
  person1(name: '홍길동'); // 홍길동, null
  person1(age: 30); // null, 30
  person1(); // null, null
 
  person2('홍길동'); // 홍길동, null
  person2('홍길동', age: 28); // 홍길동, 28
}

 

// Named Optional Parameters
void person({String name=''int age = 0}){
  // {} 파라미터를 사용하면 반드시 초기화된 값이나 required 가 필요하다.
  print('${name}, ${age}');
}
 
main(){
  person(name: '홍길동', age: 25); // 홍길동, 25
  person(name: '홍길동'); // 홍길동, 0
  person(age: 25); // , 25
  person(); // , 0
}

 

익명함수 및 람다식

익명함수의 기본 형태는 다음과 같다.

(매개변수명) { 표현식; }

 

람다식의 형태는 다음과 같다.

(매개변수명) => 표현식; // Javascript 에서는 화살표 함수라고 한다.

 

다트에서 대부분의 함수는 이름을 가지고 있다. 이름 없는 함수로 알려진 익명 함수를 생성할 수 있다.

다트에서 이름이 없는 함수를 익명함수, 람다, 또는 클로저(closure)라고 부른다.

void printElement(int element) {
  print(element);
}
 
main(){
  var list = [123];
 
  // Pass printElement as a parameter.
  list.forEach(printElement); // 함수명을 매개변수로 전달
 
  /* 익명함수(Anonymous Functions) 문법
  (parameter_list){
   statement(s); // 함수 구현부
  }
  */
  const List = ['apples''bananas''oranges'];
  List.forEach((item) {
    print('${List.indexOf(item)}: $item');
  });
  // 만약 익명함수가 한줄이거나 return문이 한줄일 경우 중괄호를 화살표로 바꿔줄 수 있다.
 
  // (파라미터) => 한줄짜리 함수문장;
  list.forEach((item) => print('${list.indexOf(item)}: $item'));
  // The above example defines an anonymous function with an untyped parameter, item.
  // The function, invoked for each item in the list,
  // prints a string that includes the value at the specified index.
}

 

 

'Flutter 앱 > Dart 언어' 카테고리의 다른 글

Dart Class(클래스)  (0) 2022.06.27
Dart Asynchronous programming(비동기 프로그래밍)  (0) 2022.06.23
Dart Type 검사(is, is!)  (0) 2022.06.22
Dart const, final  (0) 2022.06.22
Dart 변수 var, dynamic, num, int, double  (0) 2022.06.22
블로그 이미지

Link2Me

,
728x90

The instanceof-operator is called is in Dart.

is : 같은 타입이면 true

is! : 다른 타입이면 true

 

main() {
  int x = 10;
  // is 키워드를 사용하면, 해당 참조가 주어진 타입인지 검사
  if(x is int){
    print('정수');
  }
}

 

void main() {
  var value = 2;
  print(value is int);
  print(value is! int);
}

 

 

class Foo {
  @override
  Type get runtimeType => String;
}
 
main() {
  var foo = Foo();
  if (foo is Foo) {
    print("It's a foo!");
  }
  print("Type is ${foo.runtimeType}");
}

 

 

To check the type of a variable in Flutter and Dart, you can use the runtimeType property.

플러터와 다트에서 변수의 타입을 검사하기 위해, runtimeType 프로퍼티를 사용할 수 있다.

void main(){
  var a = 'Apple';
  var b = 100;
  var c = [12345];
  var d = {
    "name""John Doe",
    "age" : 40
  };
  var e = 1.14;
  
  print(a.runtimeType);
  print(b.runtimeType);
  print(c.runtimeType);
  print(d.runtimeType); 
  print(e.runtimeType);
}

 

class Person {
  var s = '홍길동';
  var d = 5.5;
  var r = true;
}
 
main() {
  Person p = Person(); // new 생략 가능
  print('Data type of s : ${p.s.runtimeType}');
  print('Data type of d : ${p.d.runtimeType}');
  print('Data type of r : ${p.r.runtimeType}');
}

 

class Shape {
  String color;
  Shape({required this.color}); // 생성자
}
 
class Circle extends Shape {
  double radius;
  Circle({color, required this.radius}) : super(color: color);
}
 
class Rectangle extends Shape {
  double length;
  double width;
  Rectangle({color, required this.length, required this.width}) : super(color: color);
}
 
void main() {
  Circle circle = Circle(color: 'red', radius: 10);
 
  print(circle is Circle); // true
  print(circle is Shape); // true
  print(circle is Rectangle); // false
}

참조 : https://www.woolha.com/tutorials/dart-getting-runtime-type-of-object

 

 

assert() 함수는 계산 결과가 참인지 거짓인지 검사한다.

https://dartpad.dartlang.org/ 사이트에서는 동작되지 않더라.

 

'Flutter 앱 > Dart 언어' 카테고리의 다른 글

Dart Class(클래스)  (0) 2022.06.27
Dart Asynchronous programming(비동기 프로그래밍)  (0) 2022.06.23
Dart function(함수)  (0) 2022.06.22
Dart const, final  (0) 2022.06.22
Dart 변수 var, dynamic, num, int, double  (0) 2022.06.22
블로그 이미지

Link2Me

,
728x90

둘 다 불변의 상수를 의미하는 keyword 이지만 const 가 더욱 불변의 강도가 강하다.

final : final 지시어는 어떤 변수를 참조하는 값이 한번 설정되면 다른 값으로 변경될 수 없다는 것을 의미한다.

          run-time constant 이며, APP 실행과정에서 값이 정해진다.

const : 한번만 설정할 수 있지만 compile-time constant로 컴파일 타임에 그 값을 알 수 있어야 한다.

            Java 언어에서는 public static final 이라는 복잡한 키워드를 사용하지만,

            다트 언어에서는 const 로 단순화할 수 있다.

 

void main() {
 String name1 = '홍길동';
 print(name1);
  
 const String name2 = '홍길동'
 print(name2);
  
 final String name3 = '홍길동'
 print(name3);
  
}

 

 

void main() {
  final name = '홍길동'// 타입 생략 가능
  print(name);
}
 

 

 

void main() {
 String name1 = '홍길동';
 name1 = '이순신';
 print(name1);
  
 const String name2 = '홍길동'
 name2 = '이순신'// 에러
 print(name2);
  
 final String name3 = '홍길동'
 name3 = '이순신'// 에러
 print(name3);
  
}

 

The Const keyword in Dart behaves exactly like the final keyword. 

The only difference between final and const is that the const makes the variable constant from compile-time only. Using const on an object, makes the object’s entire deep state strictly fixed at compile-time and that the object with this state will be considered frozen and completely immutable.

void main() {
  final x1 = DateTime.now();
  print(x1);
  
  // Error: Cannot invoke a non-'const' constructor where a const expression is expected.
  const x2 = DateTime.now(); // 컴파일 타임에 값을 알 수 없어 에러 발생
}

DateTiem.now()는 APP 실행시 정해지는 값이다.

 

'Flutter 앱 > Dart 언어' 카테고리의 다른 글

Dart Class(클래스)  (0) 2022.06.27
Dart Asynchronous programming(비동기 프로그래밍)  (0) 2022.06.23
Dart function(함수)  (0) 2022.06.22
Dart Type 검사(is, is!)  (0) 2022.06.22
Dart 변수 var, dynamic, num, int, double  (0) 2022.06.22
블로그 이미지

Link2Me

,
728x90

다트(dart) 언어는 https://dartpad.dartlang.org/ 에서 연습하면 된다.

IntelliJ IDEA community 툴을 사용하는 경우에는 https://link2me.tistory.com/2208 게시글을 참조하면 Dart 를 이용할 수 있다.

 

다트(dart) 문법은 main() 함수가 진입점(entry point)이다.

 

다트(dart)의 모든 것은 객체(object)이다.

다트의 변수는 참조(reference)를 저장한다.

 

다트에서는 변수 선언을 두가지 방법으로 할 수 있다.

var x;  // x가 숫자 타입인 경우에도 x의 값은 0이 아니라 null 이다.

String x; // <어떤 특정 타입> x;

 

다트는 정적 타입과 동적 타입(var, dynamic) 둘다 제공한다.

변수명 앞에 타입을 선언한다.

변수 종류를 Type 또는 자료형이라고 한다.

var는 할당을 통해 타입이 결정되면 그 이후에는 타입을 변경할 수 없다. 따라서 진정한 의미에서 동적 타입은 아니다.

우변 변수나 데이터의 타입을 신경쓰지 않아도 다트 컴파일러에서 추론한다.

 

var 타입은 최초 할당된 값을 참고하여 해당 변수의 타입을 추론한다.

void main() {
 var x = 10// 타입추론으로 x 가 int 형으로 설정된다.
 print(x);
 x = 5.5// 정수형으로 결정된 x 변수에 double 형을 할당 불가
 print(x);
}

 

int 와 double은 모두 num 의 서브클래스이다.

void main() {
 num x = 10// num 은 int 와 double 의 상위 클래스
 print(x);
 x = 5.5// double 형 할당 가능
 print(x);
}

final num x = 10;

final 지시어는 어떤 변수를 참조하는 값이 한번 설정되면 다른 값으로 변경될 수 없다는 의미이다.

 

 

dynamic 은 x가 시간에 따라 참조하는 내용이 변경될 수 있음을 다트에게 알려준다.

또는 최상위 클래스인 Object 를 사용해도 된다.

void main() {
 dynamic x = 10
 print(x);
 x = 5.5// double 형 할당 가능
 print(x);
}

 

int 와 double 클래스의 toString() 메소드를 사용해 숫자를 문자열로 바꿀 수 있다.

void main() {
 int x = 10// 정수형
 double y = 5.5// 실수형
 String sx = x.toString(); // 문자열
 String sy = y.toString(); // 문자열
 
 print(x);
 print(y);
 print(sx);
 print(sy);
}

 

int 와 double 클래스의 parse() 메소드를 사용해 문자열을 숫자로 바꿀 수 있다.

void main() {
 String sx = '10'
 String sy = '5.5'
 int x = int.parse(sx); 
 double y = double.parse(sy); 
 
 print(sx);
 print(sy);
 print(x);
 print(y);
}

 

null 을 할당할 수 있다.

void main(){
    double? number = 4.0;
    print(number);
 
    number = 2.0;
    print(number);
    
    number = null;
    print(number);
    
    number ??= 3.0// number가 null 이면 3.0으로 결과를 반환하라.
    print(number);
 
}

 

 

 
void main(){
    int number = 1;
 
    print(number is int);
    print(number is String);
 
    print(number is! int);
    print(number is! String);
}

 

 

'Flutter 앱 > Dart 언어' 카테고리의 다른 글

Dart Class(클래스)  (0) 2022.06.27
Dart Asynchronous programming(비동기 프로그래밍)  (0) 2022.06.23
Dart function(함수)  (0) 2022.06.22
Dart Type 검사(is, is!)  (0) 2022.06.22
Dart const, final  (0) 2022.06.22
블로그 이미지

Link2Me

,
728x90

SliverAppBar를 사용하면 스크롤 시 앱바의 크기가 줄어들거나 색이 변하는 등의 효과를 넣을 수 있다.

 

Scaffold의 AppBar를 지정하지 않고 body 에 CustomScrollView의 인스턴스를 저장했다.

SliverFillRemaining 위젯은 하나의 정적인 화면을 구성할 때 사용한다.

import 'package:flutter/material.dart';
 
class SliverPage extends StatelessWidget {
  const SliverPage({Key? key}) : super(key: key);
 
  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      slivers: [
        SliverAppBar(
          pinned: true// 축소될 때 AppBar가 고정될지 사라질지 설정
          expandedHeight: 180.0// 확대될 때의 최대 높이
          flexibleSpace: FlexibleSpaceBar(  // 확대/축소되는 영역의 UI 
            title: Text('Sliver'),
            background: Image.asset(
              'assets/images/header-01.png',
              fit: BoxFit.cover,
            ),
          ),
        ),
        SliverFillRemaining( // 내용 영역
          child: Center(
            child: Image.asset('assets/images/nature-01.png'),
          ),
        ),
      ],
    );
  }
}

 

SliverGrid 예제

이미지를 추가하는 과정에서 여백이 생겨 보기가 좋지 않았다.

이미지를 대략 일정한 높이와 너비로 미리 맞춰 준비하는 것이 필요했다.

그리고 옵션을 주지 않으면 정사각형 이미지를 추가하므로

childAspectRatio: (1.26 / 1), // (itemWidth / itemHeight) 높이와 너비에 대한 비율로 계산하여 표기하는 것이 중요하다.

import 'package:flutter/material.dart';
 
class SliverPage extends StatelessWidget {
  const SliverPage({Key? key}) : super(key: key);
 
  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      slivers: [
        SliverAppBar(
          pinned: true// 축소될 때 AppBar가 고정될지 사라질지 설정
          expandedHeight: 180.0// 확대될 때의 최대 높이
          flexibleSpace: FlexibleSpaceBar(
            // 확대/축소되는 영역의 UI
            title: Text('Sliver'),
            background: Image.asset(
              'assets/images/header-01.png',
              fit: BoxFit.cover,
            ),
          ),
        ),
        SliverGrid(
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 2,
            childAspectRatio: (1.26 / 1), // (itemWidth / itemHeight)
          ),
          delegate: SliverChildBuilderDelegate(
            (BuildContext context, int index) {
              return Container(
                alignment: Alignment.center,
                child: Image.asset('assets/images/nature-0${index}.png'),
              );
            },
            childCount: 6,
          ),
        ),
        //Sliver 2
        SliverGrid(
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 3,
            ),
            delegate: SliverChildListDelegate(
              [
                Container(color: Colors.red, height: 150.0),
                Container(color: Colors.purple, height: 150.0),
                Container(color: Colors.green, height: 150.0),
                Container(color: Colors.cyan, height: 150.0),
                Container(color: Colors.indigo, height: 150.0),
                Container(color: Colors.black, height: 150.0),
              ],
            )),
      ],
    );
  }
}

 

이미지 가져오는 과정

assets 폴더와 서브 폴더로 images 를 추가하고 이곳에 이미지를 추가했다.

 

pubspec.yaml 파일 내에 아래와 같이 이미지를 등록해줘야 한다.

 

 

SliverToBoxAdapter(
  child: Container(
    height: 100.0,
    child: ListView.builder(
        scrollDirection: Axis.horizontal,
        itemCount: 10,
        itemBuilder: (context, index) {
          return Container(
            width: 100.0,
            child: Card(
              child: Text('data-${index + 1}'),
            ),
          );
        }),
  ),
),
SliverPadding(
  padding: EdgeInsets.all(2.0),
  sliver: SliverList(
    delegate: SliverChildListDelegate(
      [
        Card(
          child: Text('data'),
        ),
        Card(
          child: Text('data'),
        ),
        Card(
          child: Text('data'),
        ),
        Card(
          child: Text('data'),
        ),
      ],
    ),
  ),
),

 

 

'Flutter 앱 > Layout' 카테고리의 다른 글

Flutter Column 위젯  (0) 2022.06.24
Flutter Container 위젯  (0) 2022.06.24
Flutter Drawer Widget  (0) 2022.06.16
Flutter Layout  (0) 2021.12.26
Flutter Theme(테마) 변경  (0) 2021.12.24
블로그 이미지

Link2Me

,
728x90

플러터 위젯 상태(state)를 관리하는 기본 방법은 setState인데, 이는 state를 업데이트 하고 위젯을 다시 빌드하는 메소드이다. 

※ Class 내에서 사용하는 function 은 메소드로 통칭한다.

부모 자식 위젯간에는 유용하지만 위젯 Depth가 커지면, 즉 조부모 → 손자 간에는 데이터 전달이 쉽지 않다.

앱의 규모가 커지면 코드는 복잡해지고 효과적인 상태 관리가 필요하다는 것을 느끼게 된다.

 

https://pub.dev/packages/provider/install 에서 최신 버전을 확인하고 pubspec.yaml 파일에 추가한다.

dependencies:
  provider: ^6.0.3

import 'package:provider/provider.dart';

를 하면 기본 준비 완료이다.

 

기존 main 함수

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

 

변경 main 함수

void main() {
  runApp(
    MultiProvider(providers: [
      ChangeNotifierProvider(create: (c) => Counter()),
    ],
    child: const MyApp(),
    )
  );
}

 

상태관리 클래스를 추가한다.

provider/counter_provider.dart

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
 
class CounterProvider extends ChangeNotifier {
  int _count = 0;
 
  int get count => _count;
 
  void increment() {
    _count++;
    notifyListeners();
  }
 
  void decrement() {
    _count--;
    notifyListeners();
  }
}
 

 

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter/foundation.dart';
import 'counterview_home.dart';
import './provider/counter_provider.dart';
 
void main() {
  runApp(
    MultiProvider(providers: [
      ChangeNotifierProvider(create: (c) => CounterProvider()),
    ],
    child: const MyApp(),
    )
  );
}
 
class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);
 
  @override
  State<MyApp> createState() => _MyAppState();
}
 
class _MyAppState extends State<MyApp> {
 
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      // home: const DrawerMenu(title: 'Drawer Demo'),
      home: CounterView_Home(),
    );
  }
}

 

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import './provider/counter_provider.dart';
 
class CounterView_Home extends StatelessWidget {
  const CounterView_Home({Key? key}) : super(key: key);
 
  @override
  Widget build(BuildContext context) {
    CounterProvider counterProvider = Provider.of<CounterProvider>(context,listen: false);
    //print('CounterView rendering');
    return Scaffold(
      appBar: AppBar(
        title: const Text('Provider Counter Example'),
      ),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          mainAxisAlignment: MainAxisAlignment.center,
          children: const <Widget>[
            Text('You have pushed the button this many times:'),
            Count(),
          ],
        ),
      ),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          IconButton(
            //onPressed: () => context.read<CounterProvider>().increment(),
            onPressed: () => counterProvider.increment(),
            icon: Icon(Icons.add),
          ),
          IconButton(
              //onPressed: () => context.read<CounterProvider>().decrement(),
              onPressed: () => counterProvider.decrement(),
              icon: Icon(Icons.remove)),
        ],
      ),
    );
  }
}
 
class Count extends StatelessWidget {
  const Count({Key? key}) : super(key: key);
 
  @override
  Widget build(BuildContext context) {
    return Text('${context.watch<CounterProvider>().count}',
        key: const Key('counterState'),
        style: Theme.of(context).textTheme.headline4);
  }
}

 

 

블로그 이미지

Link2Me

,
728x90

상대적으로 적은 양의 키-값 데이터를 저장하려고 한다면, shared_preferences 플러그인을 사용한다.

 

https://pub.dev/packages/shared_preferences/install 에서 최신 버전을 찾아서 pubspec.yaml 내에 추가한다.

dependencies:
  shared_preferences: ^2.0.15

 

Dart 코드에 아래 한줄을 import 한다.

import 'package:shared_preferences/shared_preferences.dart';

 

Save Data

// shared preferences 얻기
final prefs = await SharedPreferences.getInstance();
 
// 값 저장하기
prefs.setInt('counter', counter);

 

Read Data

// Save Data
final prefs = await SharedPreferences.getInstance();
prefs.setInt('counter'0);
prefs.setDouble('width'20.5);
prefs.setBool('isAdmin'true);
prefs.setString('userName''dev-yakuza');
prefs.setStringList('alphabet', ['a''b''c''d']);
 
// Read Data
final prefs = await SharedPreferences.getInstance();
final counter = prefs.getInt('counter') ?? 0;
final width = prefs.getDouble('width') ?? 10.5;
final isAdmin = prefs.getBool('isAdmin') ?? false;
final userName = prefs.getString('userName') ?? '';
final alphabet = prefs.getStringList('alphabet') ?? [];
final data = prefs.get('userInfo') : {};

 

Remove Data

final prefs = await SharedPreferences.getInstance();
prefs.remove('counter'); // 데이터 삭제

모든 데이터 삭제는 prefs.clear();

 

 

예제

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
 
class SharedPreferencesDemo extends StatefulWidget {
  const SharedPreferencesDemo({Key? key}) : super(key: key);
 
  @override
  State<SharedPreferencesDemo> createState() => _SharedPreferencesDemoState();
}
 
class _SharedPreferencesDemoState extends State<SharedPreferencesDemo> {
  // Future 은 미래의 값을 의미한다.
  // Future는 비동기 작업의 결과를 나타내며
  // 미완료(value를 생성하기 전)또는 완료(value 생성)의 두 가지 상태를 가질 수 있다.
  // Dart에서는 비동기 작업을 수행하기 위해 Future클래스와 async 및 await 키워드를 사용할 수 있다.
  final Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
  late Future<int> _counter;
 
  // async : 함수 본문 앞에 키워드로 사용하여 비동기로 표시
  // Future는 Future<타입명> 이런식으로 타입을 명시해야 한다.
  Future<void> _incrementCounter() async {
    final SharedPreferences prefs = await _prefs;
    final int counter = (prefs.getInt('counter') ?? 0+ 1;
 
    setState(() {
      _counter = prefs.setInt('counter', counter).then((bool success) {
        return counter;
      });
    });
  }
 
  @override
  void initState() {
    super.initState();
    _counter = _prefs.then((SharedPreferences prefs) {
      return prefs.getInt('counter') ?? 0;
    });
  }
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('SharedPreferences Demo'),
      ),
      body: Center(
          child: FutureBuilder<int>(
              future: _counter,
              builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
                switch (snapshot.connectionState) {
                  case ConnectionState.waiting:
                    return const CircularProgressIndicator();
                  default:
                    if (snapshot.hasError) {
                      return Text('Error: ${snapshot.error}');
                    } else {
                      return Text(
                        '${snapshot.data} time${snapshot.data == 1 ? '' : 's'}.\n'
                       'This should persist across restarts.',
                      );
                    }
                }
              })),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

 

블로그 이미지

Link2Me

,
728x90

안드로이드 앱에서는 화면에 표시되는 것을 View 클래스로 정의한다.

화면에 텍스트를 보여주고 이미지를 표시하고 버튼을 클릭하는 등의 모든 표현과 사용자 상호작용은 View를 통해 일어난다.

 

플러터에서는 모든 화면 표시와 사용자 상호작용은 위젯을 사용한다.

화면에 위젯을 배치하고 위젯이 정보를 표시하고 사용자의 입력을 받거나 네트워크에서 받아온 결과를 출력한다.

 

플러터의 위젯은 크게 Stateless Widget 과 Stateful Widget 으로 구별한다.

Stateless Widget은 표면 표시용 위젯이다. 위젯이 로딩되어 화면에 표시된 이후에는 사용자 이벤트나 동작이 있어도 내용을 변경할 수 없다.

Stateful Widget은 변경 가능한 상태(State)를 가진다.

Stateful Widget 은 UI가 동적으로 변경될 수 있는 경우에 유용하다.

 

StatelessWidget은 상태가 변경될 때 플러터 코어 프레임워크에 의해 자동으로 다시 랜더링도지 않는다.

StatefulWidget의 상태가 변하면 변경되는 원인에 관계없이 특정 생명주기 이벤트가 발생한다. 생명주기 이벤트 함수 호출은 트리거한 결과 위젯이 차지하는 화면의 일부를 다시 랜더링한다.

import 'package:flutter/material.dart';
 
class LView extends StatefulWidget {
  const LView({Key? key, required this.title}) : super(key: key);
 
  final String title; // 부모 위젯으로부터 전달받은 변수
  // final 지시어는 어떤 변수를 참조하는 값이 한번 설정되면
  // 다른 값으로 변경될 수 없다는 의미이다.
 
  @override
  State<LView> createState() => _LViewState();
}
 
class _LViewState extends State<LView> {
  // 상태를 변경하므로 변경가능한 것들은 선언할 수 있다.
 
  int _counter = 0// _ 를 붙이면 private 변수
  // var : 변수 할당시 타입이 지정된다. dart 컴퍼일러에서 타입을 추론한다.
  // dynamic : 타입을 특정하지 않는다.
  List<String> name = ['주영훈''홍길동''이순신'];
  var like = [000];
 
  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }
 
  // widget으로 StatefulWidget에 선언한 프로퍼티들에 접근이 가능
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: ListView.builder(
        itemCount: name.length,
        itemBuilder: (c, i){
          return ListTile(
            leading: Text(like[i].toString()),
            title: Text(name[i]),
            trailing: ElevatedButton(
              child: Text('좋아요'),
              onPressed: (){
                setState(() {
                  like[i]++;
                });
              },
            ),
          );
        }),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Text(_counter.toString()),
        // child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

 

플러터 UI는 코드에서 만들어진다.

 

 

 

 

 

 

블로그 이미지

Link2Me

,
728x90

만약 Upgrade가 안되면 강제로 업그레이드하는 명령어를 사용하면 된다.

flutter upgrade --force

 

updated 2023.10.19

 

 

updated 2022.6.17

Flutter 업그레이드를 했더니 3.0.2 버전으로 업그레이된다.

- 하나의 코드로 안드로이드, IOS, 웹, 윈도우, 맥OS, 리눅스 총 6개의 플랫폼의 빌드 배포가 가능해졌다.

  Flutter 3.0.0 업그레이드를 통해 Linux 및 macOS 앱을 지원한다.

- Flutter 3.0 버전에서는 Material Design3 버전을 지원한다.

- 그동안 Firebase Console는 안드로이드, IOS, 웹, 유니티만을 지원 했으나 Flutter 도 공식적으로 지원하게 되었다.

- Dart 2.17 Update 지원 : Enum 클래스에 멤버 변수 및 함수를 추가하여 사용할 수 있게 되었다.

 

 

기본적으로 설치되는 앱은 코틀린 버전이 낮게 설정되어 있다.

버전을 올려서 컴파일을 해야 컴파일이 된다.

 

블로그 이미지

Link2Me

,
728x90

android DrawerLayout 을 구글 검색하면 어떤 사항인지 정보가 나온다.

 

https://api.flutter.dev/flutter/material/Drawer-class.html 를 검색하면 매뉴얼로 제공되는 정보를 알 수 있다.

Drawer Navigation 메뉴는 앱의 Main UI 로 구현하는 경우가 많다.

 

StatefulWidget 으로 전환하는 것도 쉽게 가능하다. StatelessWidget 에 마우스를 놓고 변경하면 된다.

사용자 반응 앱을 만들려면 StatefulWidget 으로 구성한다.

// lib 폴더에서 dartfile을 선택하고 drawer 입력하면 drawer.dart 파일이 생성된다.
import 'package:flutter/material.dart';
 
// stless 입력하고 TAB키를 누르면 자동완성 Class 가 생성된다.
class Drawer extends StatelessWidget {
  const Drawer({Key? key}) : super(key: key);
 
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

 

main.dart 파일에서 Class 분리하여 별도 파일 생성하여 처리한다.

import 'package:codingapple/drawer.dart';
import 'package:flutter/material.dart';
 
void main() {
  runApp(const MyApp());
}
 
class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);
 
  @override
  State<MyApp> createState() => _MyAppState();
}
 
class _MyAppState extends State<MyApp> {
 
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const DrawerMenu(title: 'Drawer Demo'),
    );
  }
}

 

drawer.dart

// lib 폴더에서 dartfile을 선택하고 drawer 입력하면 drawer.dart 파일이 생성된다.
import 'package:flutter/material.dart';
 
import 'dialog.dart';
 
// stful 입력하고 TAB키를 누르면 자동완성 Class 가 생성된다.
// 예약어에 해당하는 클래스명은 피한다.
class DrawerMenu extends StatefulWidget {
  const DrawerMenu({Key? key, required this.title}) : super(key: key);
 
  final String title; // 부모 위젯으로부터 전달받은 변수
 
  @override
  State<DrawerMenu> createState() => _DrawerMenuState();
}
 
class _DrawerMenuState extends State<DrawerMenu> {
  @override
  Widget build(BuildContext context) {
    // https://api.flutter.dev/flutter/material/Drawer-class.html 에서
    // 복사하여 Container를 덮어쓰기 한다.
    // 아래 코드와 비교하면 약간 수정된 사항이 있으니 참고하시라.
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      drawer: Drawer(
        child: ListView(
          padding: EdgeInsets.zero,
          children: <Widget>// const <Widget> 에서 const 를 삭제한다.
            DrawerHeader(
              decoration: BoxDecoration(
                color: Colors.blue,
              ),
              child: Text(
                'Drawer Header',
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 24,
                ),
              ),
            ),
            ListTile(
              leading: Icon(Icons.message),
              title: Text('Messages'),
              tileColor: Colors.red,
              trailing: Icon(Icons.more_vert),
            ),
            ListTile(
              leading: Icon(Icons.account_circle),
              title: Text(
                'Profile',
                style: TextStyle(
                  fontSize: 16,
                  fontWeight: FontWeight.w700,
                  color: Colors.blue,
                ),
              ),
              onTap: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) => LView(title: '연락처 앱'),
                  ),
                );
              },
            ),
            ListTile(
              leading: Icon(Icons.settings),
              title: Text('Settings'),
            ),
            ListTile(
              leading: Icon(Icons.account_circle),
              title: Text('Two-line ListTile'),
              subtitle: Text('Here is a second line'),
              trailing: Icon(Icons.more_vert),
              isThreeLine: true,
            ),
          ],
        ),
      ),
    );
  }
}

 

Android 앱 개발시 사용하는 에뮬레이터를 공용으로 사용할 수 있으며, 위 코드의 결과 UI 이다.

 

'Flutter 앱 > Layout' 카테고리의 다른 글

Flutter Container 위젯  (0) 2022.06.24
Flutter CustomScrollView  (0) 2022.06.21
Flutter Layout  (0) 2021.12.26
Flutter Theme(테마) 변경  (0) 2021.12.24
Flutter BottomNavigationBar 예제  (0) 2021.12.22
블로그 이미지

Link2Me

,
728x90

플러터 팝업창에서 입력한 값을 전달받아 Array 를 업데이트하는 예제이다.

부모 위젯에서 만든 함수를 자식 위젯으로 인자로 넘겨서, 자식 위젯에서 입력한 변수를 받아서 매개변수로 넘기면 ListView가 업데이트되는 코드이다.

import 'package:flutter/material.dart';
 
class LView extends StatefulWidget {
  const LView({Key? key, required this.title}) : super(key: key);
 
  final String title; // 부모 위젯으로부터 전달받은 변수
 
  @override
  State<LView> createState() => _LViewState();
}
 
class _LViewState extends State<LView> {
  var person = ['강감찬''홍길동''이순신''유관순'];
  var clickme = [0000];
 
  addNameArr(name) { // 자식 위젯에서 입력한 값을 전달받아 Array Update
    setState(() {
      person.add(name);
      clickme.add(0);
    });
  }
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: ListView.builder(
          itemCount: person.length,
          itemBuilder: (c, i) {
            return ListTile(
              leading: Text(clickme[i].toString()),
              title: TextButton(
                child: Text(person[i]),
                onPressed: () => showDialog<String>(
                    context: context,
                    barrierDismissible: false// Dialog를 제외한 다른 화면 터치 x
                    builder: (context) {
                      return DialogUI(
                          Cnt: clickme[i], Name: person[i]); // 변수 넘겨주기
                    }),
              ),
              trailing: ElevatedButton(
                child: Text('좋아요'),
                onPressed: () {
                  setState(() {
                    clickme[i]++;
                  });
                },
              ),
            );
          }),
      floatingActionButton: FloatingActionButton(
        onPressed: () => showDialog(
            context: context,
            builder: (context) {
              return InsertDialogUI(addNameArr: addNameArr);
            }),
        child: const Icon(Icons.add),
      ),
    );
  }
}
 
class DialogUI extends StatelessWidget {
  const DialogUI({Key? key, this.Cnt, this.Name}) : super(key: key);
  final Cnt; // 부모 위젯으로부터 전달받은 변수 등록
  final Name; // 부모 위젯으로부터 전달받은 변수 등록
 
  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      // RoundedRectangleBorder - Dialog 화면 모서리 둥글게 조절
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
      backgroundColor: Colors.white,
      //Dialog Main Title
      title: Column(
        children: <Widget>[
          Text("팝업 메시지"),
        ],
      ),
      //
      content: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Text(
            "카운트 횟수 ${Cnt}, 이름 : ${Name}",
          ),
        ],
      ),
      actions: <Widget>[
        ElevatedButton(
          onPressed: () => Navigator.pop(context, 'Cancel'),
          child: const Text('Cancel'),
        ),
        ElevatedButton(
          onPressed: () => Navigator.pop(context, 'OK'),
          child: const Text('OK'),
        ),
      ],
    );
  }
}
 
class InsertDialogUI extends StatelessWidget {
  InsertDialogUI({Key? key, this.addNameArr}) : super(key: key);
  final addNameArr; // 부모 위젯에서 전달받은 변수(함수)
 
  final _textFieldController = TextEditingController();
 
  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: Text('Item 추가'),
      content: TextField(
        controller: _textFieldController,
        decoration: const InputDecoration(hintText: "성명 입력하세요"),
      ),
      actions: [
        ElevatedButton(
          onPressed: () => Navigator.pop(context, 'Cancel'),
          child: const Text('Cancel'),
          style: ElevatedButton.styleFrom(
            primary: Colors.green,
            onPrimary: Colors.white,
          ),
        ),
        ElevatedButton(
          onPressed: () {
            addNameArr(_textFieldController.text);
            Navigator.pop(context);
          },
          child: const Text('OK'),
          style: ElevatedButton.styleFrom(
            primary: Colors.green,
            onPrimary: Colors.white,
          ),
        ),
      ],
    );
  }
}

 

 

블로그 이미지

Link2Me

,
728x90

특정 버튼(이미지, List Item 등)을 눌렀을 때 팝업되는 메시지 구현 예제이다.

import 'package:flutter/material.dart';
 
class LView extends StatefulWidget {
  const LView({Key? key, required this.title}) : super(key: key);
 
  final String title; // 부모 위젯으로부터 전달받은 변수
 
  @override
  State<LView> createState() => _LViewState();
}
 
class _LViewState extends State<LView> {
  var name = ['강감찬''홍길동''이순신','유관순'];
  var clickme = [0000];
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: ListView.builder(
         itemCount: name.length,
          itemBuilder: (c, i) {
            return ListTile(
              leading: Text(clickme[i].toString()),
              title: Text(name[i]),
              trailing: ElevatedButton(
                child: Text('좋아요'),
                onPressed: () {
                  setState(() {
                    clickme[i]++;
                  });
                },
              ),
            );
          }),
      floatingActionButton: FloatingActionButton(
        onPressed: () => ShowDialog(),
        // child: Text(_counter.toString()),
        child: const Icon(Icons.add),
      ),
    );
  }
 
  ShowDialog() {
    showDialog<String>(
        context: context,
        barrierDismissible: false// Dialog를 제외한 다른 화면 터치 x
        builder: (BuildContext context) {
          return AlertDialog(
            // RoundedRectangleBorder - Dialog 화면 모서리 둥글게 조절
            shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(10.0)),
            backgroundColor: Colors.white,
            //Dialog Main Title
            title: Column(
              children: <Widget>[
                Text("팝업 메시지"),
              ],
            ),
            //
            content: Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Text(
                  "AlertDialog Content",
                ),
              ],
            ),
            actions: <Widget>[
              ElevatedButton(
                onPressed: () => Navigator.pop(context, 'Cancel'),
                child: const Text('Cancel'),
              ),
              ElevatedButton(
                onPressed: () => Navigator.pop(context, 'OK'),
                child: const Text('OK'),
              ),
            ],
          );
        });
  }
}
 

 

값을 넘겨주는 걸 처리하는 방법에 대해 알아보자.

부모 위젯 → 자식 위젯 으로 값을 넘기는 방법

Class 간에 값 전달 방식으로 값, 함수를 넘기므로 아래 코드에서 해당 주석 부분을 잘 살펴보면 된다.

import 'package:flutter/material.dart';
 
class LView extends StatefulWidget {
  const LView({Key? key, required this.title}) : super(key: key);
 
  final String title; // 부모 위젯으로부터 전달받은 변수
 
  @override
  State<LView> createState() => _LViewState();
}
 
class _LViewState extends State<LView> {
  var name = ['강감찬''홍길동''이순신','유관순'];
  var clickme = [0000];
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: ListView.builder(
         itemCount: name.length,
          itemBuilder: (c, i) {
            return ListTile(
              leading: Text(clickme[i].toString()),
              title: Text(name[i]),
              trailing: ElevatedButton(
                child: Text('좋아요'),
                onPressed: () {
                  setState(() {
                    clickme[i]++;
                  });
                },
              ),
            );
          }),
      floatingActionButton: FloatingActionButton(
        onPressed: () => showDialog<String>(
            context: context,
            barrierDismissible: false// Dialog를 제외한 다른 화면 터치 x
            builder: (context){
          return DialogUI(Cnt: clickme[1], Name: name[1]); // 변수 넘겨주기
        }),
        // child: Text(_counter.toString()),
        child: const Icon(Icons.add),
      ),
    );
  }
 
}
 
class DialogUI extends StatelessWidget {
  const DialogUI({Key? key, this.Cnt, this.Name}) : super(key: key);
  final Cnt; // 부모 위젯으로부터 전달받은 변수 등록
  final Name; // 부모 위젯으로부터 전달받은 변수 등록
 
  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      // RoundedRectangleBorder - Dialog 화면 모서리 둥글게 조절
      shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(10.0)),
      backgroundColor: Colors.white,
      //Dialog Main Title
      title: Column(
        children: <Widget>[
          Text("팝업 메시지"),
        ],
      ),
      //
      content: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Text(
            "AlertDialog Content : 카운트 횟수 ${Cnt}, 이름 : ${Name}",
          ),
        ],
      ),
      actions: <Widget>[
        ElevatedButton(
          onPressed: () => Navigator.pop(context, 'Cancel'),
          child: const Text('Cancel'),
        ),
        ElevatedButton(
          onPressed: () => Navigator.pop(context, 'OK'),
          child: const Text('OK'),
        ),
      ],
    );
  }
}

 

이제 ListView Item 에서 클릭을 하면 팝업창이 뜨는 예제로 전환해보자.

title : Text 를 title : TextButton 으로 변경하고 onPressed 이벤트를 추가한다.

import 'package:flutter/material.dart';
 
class LView extends StatefulWidget {
  const LView({Key? key, required this.title}) : super(key: key);
 
  final String title; // 부모 위젯으로부터 전달받은 변수
 
  @override
  State<LView> createState() => _LViewState();
}
 
class _LViewState extends State<LView> {
  var name = ['강감찬''홍길동''이순신','유관순'];
  var clickme = [0000];
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: ListView.builder(
         itemCount: name.length,
          itemBuilder: (c, i) {
            return ListTile(
              leading: Text(clickme[i].toString()),
              title: TextButton(
                  child: Text(name[i]),
                onPressed: () => showDialog<String>(
                    context: context,
                    barrierDismissible: false// Dialog를 제외한 다른 화면 터치 x
                    builder: (context){
                      return DialogUI(Cnt: clickme[i], Name: name[i]); // 변수 넘겨주기
                    }),
              ),
              trailing: ElevatedButton(
                child: Text('좋아요'),
                onPressed: () {
                  setState(() {
                    clickme[i]++;
                  });
                },
              ),
            );
          }),
      floatingActionButton: FloatingActionButton(
        onPressed: (){},
        child: const Icon(Icons.add),
      ),
    );
  }
 
}
 
class DialogUI extends StatelessWidget {
  const DialogUI({Key? key, this.Cnt, this.Name}) : super(key: key);
  final Cnt; // 부모 위젯으로부터 전달받은 변수 등록
  final Name; // 부모 위젯으로부터 전달받은 변수 등록
 
  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      // RoundedRectangleBorder - Dialog 화면 모서리 둥글게 조절
      shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(10.0)),
      backgroundColor: Colors.white,
      //Dialog Main Title
      title: Column(
        children: <Widget>[
          Text("팝업 메시지"),
        ],
      ),
      //
      content: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Text(
            "카운트 횟수 ${Cnt}, 이름 : ${Name}",
          ),
        ],
      ),
      actions: <Widget>[
        ElevatedButton(
          onPressed: () => Navigator.pop(context, 'Cancel'),
          child: const Text('Cancel'),
        ),
        ElevatedButton(
          onPressed: () => Navigator.pop(context, 'OK'),
          child: const Text('OK'),
        ),
      ],
    );
  }
}
 

 

 

'Flutter 앱 > Flutter Basic' 카테고리의 다른 글

Flutter Widget 개념(stateless, stateful)  (0) 2022.06.18
Flutter input dialog (자식 → 부모)  (0) 2022.06.15
Flutter ListView 예제1  (0) 2021.12.27
Flutter Toast 메시지  (0) 2021.12.24
Flutter 화면 이동 (Navigator)  (0) 2021.12.23
블로그 이미지

Link2Me

,
728x90

객체는 프로퍼티와 메소드의 집합이다.

객체 리터럴을 사용해서 객체를 생성하는 것은 연속된 구조체나 연관된 데이터를 일정한 방법으로 변환하고자 할 때  많이 쓰이는 방법이다.

this 키워드는 지금 동작하고 있는 코드를 가지고 있는 객체를 가리킨다. 아래 예제에서 this 는 user 객체와 동일하다. 

 

// Object(객체 데이터) : 여러 데이터를 key:value 형태로 저장한다. {}
let user = {
    name : 'Json',
    nickname : 'Link2Me',
    age : 25,
    getName: function () { // 메소드(Method)
        console.log(`이름:${this.name}, 별명:${this.nickname}, 나이:${this.age}`)
    }
};
 
console.log(user.name);
console.log(user.nickname);
console.log(user.age);
user.getName();

 

 

'React > morden javascript' 카테고리의 다른 글

[Javascript] _cloneDeep()  (0) 2022.10.05
[ES6] Spread 연산자  (0) 2022.10.02
자바스크립트 호이스팅(Hoisting)  (0) 2022.06.10
Javascript this and Class  (0) 2022.06.09
javascript 콜백 함수(callback function)  (0) 2022.05.27
블로그 이미지

Link2Me

,
728x90

변수의 선언을 해당 스코프의 맨 위로 끌어올려졌다. → 호이스팅

console.log(x);
var x = 1;
 
// 위 코드는 아래와 똑같다.
var x;
console.log(x);
= 1;

 

자바스크립트 Hoisting 이란 함수 선언부가 유효범위 최상단으로 끌어올려지는 현상을 말한다.

함수를 하단에 작성하는 것이 좋다.

 

const a = 5;
 
double();
 
function double(){
  console.log(a*2);
}

 

 

타이머 함수

setTimeout(함수, 시간); // 일정시간 후 함수 실행

clearTimeout(); // 설정된 Timeout 함수를 종료

 

setInterval(함수, 시간); // 시간 간격마다 함수 실행

clearInterval();  // 설정된 Interval 함수를 종료

 

 

const timer = setTimeout(()=> {
  console.log('Hello world');
},2000);
 
const h1El = document.querySelector('h1');
h1El.addEventListener('click', ()=> {
  clearTimeout(timer);
});

 

'React > morden javascript' 카테고리의 다른 글

[ES6] Spread 연산자  (0) 2022.10.02
자바스크립트 Object  (0) 2022.06.11
Javascript this and Class  (0) 2022.06.09
javascript 콜백 함수(callback function)  (0) 2022.05.27
javascript 화살표 함수(람다식)  (0) 2022.05.27
블로그 이미지

Link2Me

,
728x90

node.js 에서 유틸리티를 상속하는 예제이다.

ES6 이전버전에서는 유용하겠지만 ES6 이후는 클래스 상속으로 처리하면 된다.

 

// 모듈 로딩
const util = require('util');
 
// 유틸리티 상속
function Parent() {
}
Parent.prototype.sayHello = function () {
    console.log('Hello world, from Parent Class');
}
 
function Child() {
}
util.inherits(Child,Parent); // 상속관계 지정
 
const child = new Child();
child.sayHello();

 

ES6 클래스 상속

// 모듈 로딩
// const util = require('util');
 
// 클래스 구현 : 자바스크립트에서 클래스 사용은 ES6에서부터 지원
class Parent {
    sayHello() {
        return 'Hello world, from Parent Class';
    }
}
 
// 클래스 상속
class Child extends Parent {
}
 
const child = new Child();
console.log(child.sayHello());
 

 

블로그 이미지

Link2Me

,
728x90

this는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수(self-reference variable)이다.

세부적인 사항은 https://velog.io/@realryankim/JavaScript-this%EB%9E%80 를 참조하면 도움된다.

 

// this
// 일반(normal) 함수는 호출 위치에 따라 this 정의
// 화살표(Arrow) 함수는 자신의 선언된 함수 범위에서 this 정의
 
const link2me = {
    name : 'Link2Me',
    normal: function () {
        console.log(this.name);
    },
    arrow: () => {
        console.log(this.name);
    }
}
 
link2me.normal(); // 결과 : Link2Me
link2me.arrow();  // 결과 : undefined
 
function User(name) {
    this.name = name;
}
 
User.prototype.normal = function () {
    console.log(this.name);
}
 
User.prototype.arrow = () => {
    console.log(this.name);
}
 
const link2u = new User('Link2U');
link2u.normal(); // 결과 : Link2U
link2u.arrow(); // 결과 : undefined

 

ES6 Class

- 자바스크립트에서 클래스는 프로토타입을 이용해서 만들어졌지만 ES5의 클래스 의미와는 다른 문법과 의미를 가진다.

- 자바스크립트에서 클래스 사용은 ES6에서부터 지원을 하기 시작했다. ES5까지 자바스크립트에는 클래스가 없었다.
  익스플로러에서는 해당 코드 사용이 불가능하나, 최신 브라우저를 이용할 경우 class를 지원한다.

  생김새만 클래스 구조이지, 엔진 내부적으로는 프로토타입 방식으로 작동된다.

- Class는 객체를 정의하기 위한 상태(멤버 변수)와 메서드(함수)로 구성된다.

 

클래스 내에서 정의한 메서드는 User.prototype에 저장한다.

class User {
    constructor(first, last) {
        this.firtstName = first;
        this.lastName = last;
    }
    getFullName(){
        return `${this.firtstName} ${this.lastName}`;
    }
}
 
const link2me = new User('Link2Me''Json');
link2me.getFullName();
 
console.log(link2me); // User { firtstName: 'Link2Me', lastName: 'Json' }
console.log(link2me.getFullName()); // Link2Me Json

new User('Link2Me', 'Json') 을 호출하면

1. 새로운 객체가 생성된다.

2. 넘겨받은 인수와 함께 constructor 가 자동으로 실행된다. 매개변수가 변수에 할당된다.

3. 객체의 메서드를 호출하면 메서드를 prototype 프로퍼티를 통해 가져온다.

    이 과정이 있기 때문에 객체에서 클래스 메서드에 접근할 수 있다.

 

클래스는 함수이다. alert(typeof User);

 

정확히는 생성자 메서드와 동일하다.
alert(User === User.prototype.constructor); // true

클래스 내부에서 정의한 메서드는 User.prototype에 저장된다.
alert(User.prototype.getFullName);

현재 프로토타입에는 메서드가 두 개다.
alert(Object.getOwnPropertyNames(User.prototype)); // constructor, getFullName

 

클래스 상속

// 클래스 구현 : 자바스크립트에서 클래스 사용은 ES6에서부터 지원
class Parent {
    sayHello() {
        return 'Hello world, from Parent Class';
    }
}
 
// 클래스 상속
class Child extends Parent {
}
 
const child = new Child();
console.log(child.sayHello());

 

 

 
class Person {
  constructor(name) {
    this.name = name;
  }
  sayHello() {
    console.log(`hello~ ${this.name}!`);
  }
 
  // 부모 클래스에서 클래스 필드로 메서드를 정의하면
  // 자식에서 super를 사용해서 호출할 수 없다.
  // 이 메서드는 프로토타입에 추가되는 것이 아니고, 객체에 추가된다.
  getRandom = () => {
    return Math.floor(Math.random() * 10);
  };
}
 
class Programmer extends Person {
  constructor(name, language) {
    super(name);
    this.language = language;
  }
  sayHello() {
    super.sayHello();
    console.log(`I like ${this.language}.`);
    console.log(`Your lucky number is ${super.getRandom()}`);
    // super는 프로토타입을 기반으로 동작한다.
    // super로 접근하려고 하면 에러가 발생한다. this 로 변경해보라.
  }
 
  // this로 자식에서도 찾고자 한다면 클래스 필드로 정의하면 된다.
  // getRandom = () =>
  getRandom() {
    return 20 + Math.floor(Math.random() * 10);
  }
}
 
const person1 = new Programmer('mike''javascript');
person1.sayHello();
 

 

 

 

class Person {
    age = 25;
    constructor(name) {
        this.name = name;
    }
    sayHello() { // this 인식
        console.log(`hello~ ${this.name}!`);
    }
 
    arrowName = () => {
        console.log(this.name);// this 인식
    };
 
    normalName = function(){
        console.log(this.name); // undefined
    }
}
 
class Programmer extends Person {
    constructor(name, language) {
        super(name);
        this.language = language;
    }
    sayHello() { // 메서드 오버라이딩
        super.sayHello();
        console.log(`${this.name}은 ${this.language} 언어를 좋아합니다.`);
    }
}
 
const p1 = new Person('강감찬');
p1.sayHello();
 
const p2 = new Programmer('홍길동''javascript');
p2.sayHello();
 
console.log(Person.prototype.age, Person.prototype.arrowName); // undefined undefined
console.log(Person.prototype.age, Person.prototype.normalName); // undefined undefined
 
const person1 = new Person('mike');
const person2 = new Person('jane');
person1.age = 50;
console.log(person1.age, person2.age); // 50 25
 
setTimeout(person1.arrowName, 100); // mike
setTimeout(person1.normalName, 100); // undefined
setTimeout(person2.arrowName, 100); // jane
setTimeout(person2.normalName, 100); // undefined
 

 

 

블로그 이미지

Link2Me

,
728x90

동기식 : A 실행 → A 결과 → B 실행 B 결과

- A 결과를 회신 받아서 그 다음 B를 실행한다.

- 동기식은 순차적, 직렬적으로 태스트를 수행한다.

- 동기식은 요청을 보낸 후 응답(결과물)을 받아야지만 다음 동작이 이루어지는 방식이다.

- 결과가 주어질 때까지 아무것도 하지 못하고 대기해야 하는 단점이 있을 수 있다.

// 동기식 함수 구현
function add(a, b) {
    return a + b;
}
 
// 동기식 함수 사용
let result = add(3,4);
console.log('result : ', result);

 

 

비동기식 : A 실행 → B 실행 → A 결과

- A가 비동기로 실행되고 A 결과를 기다리지 않고, 바로 B가 실행되고 난 이후 A의 결과가 나올 수 있다.

- 비동기식은 병렬적으로 태스트를 수행한다. 즉, 요청을 보낸 후 결과와는 상관없이 다음 방식이 동작하는 방식이다.

- 결과가 주어지는데 시간이 걸리더라도 그 시간동안 다른 작업을 할 수 있으므로 자원을 효율적으로 사용할 수 있다.

- 비동기식 처리 예시로는 setTimeout과 AJAX가 있다.

// 비동기식
const fs = require('fs');
fs.readFile("readme.txt""utf8"function (err,content) {
    console.log(content);
});
console.log("Reading files ...");

- 모듈 로딩 : require('모듈이름')

 

callback 함수의 결과가 나중에 실행된다.

// 비동기식 함수 구현
function add(a, b, callback) {
    let result = a + b;
    callback(result);
}
 
// 비동기식 함수 사용
add(3,4,function (result) {
    console.log('result : ', result);
});

 

블로그 이미지

Link2Me

,