728x90

수평방향으로 위젯들을 나란히 배치하는 위젯이다.

children 프로퍼티에 여러 위젯들을 나열한다.

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Row(
        mainAxisAlignment: MainAxisAlignment.start, // 가로 방향 왼쪽 정렬
        children: [
          Container(
            color: Colors.red,
            width: 100,
            height: 100,
            padding: const EdgeInsets.all(8.0),
            margin: const EdgeInsets.all(8.0),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                const Text(
                  'Container',
                ),
              ],
            ),
          ),
          Container(
            color: Colors.green,
            width: 100,
            height: 100,
            padding: const EdgeInsets.all(8.0),
            margin: const EdgeInsets.all(8.0),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                const Text(
                  'Container',
                ),
              ],
            ),
          ),
          Container(
            color: Colors.blue,
            width: 100,
            height: 100,
            padding: const EdgeInsets.all(8.0),
            margin: const EdgeInsets.all(8.0),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                const Text(
                  'Container',
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
 

 

FloatingActionButton 에 Row 위젯을 적용한 예제

floatingActionButton: Row(
  mainAxisAlignment: MainAxisAlignment.end,
  children: [
    FloatingActionButton(
        onPressed: () {},
        backgroundColor: Colors.green,
        child: const Icon(Icons.add)),
    SizedBox(width: 10, height: 10,), // 여백을 만들기 위해서 넣음.
    FloatingActionButton(
        onPressed: () {},
        backgroundColor: Colors.blue,
        child: const Text('5')),
    SizedBox(width: 10, height: 10,), // 여백을 만들기 위해서 넣음.
    FloatingActionButton(
        onPressed: () {},
        backgroundColor: Colors.pink,
        child: const Icon(Icons.remove)),
  ],
),

 

FloatingActionButton 크기 조절 예제

floatingActionButton: Row(
  mainAxisAlignment: MainAxisAlignment.end,
  children: [
    SizedBox(
      width: 30.0,
      height: 30.0,
      child: FittedBox(
        child: FloatingActionButton(
            onPressed: () {},
            backgroundColor: Colors.green,
            child: const Icon(Icons.add)),
      ),
    ),
    SizedBox(width: 10, height: 10,), // 여백을 만들기 위해서 넣음.
    SizedBox(
      width: 60.0,
      height: 60.0,
      child: FittedBox(
        child: FloatingActionButton(
            onPressed: () {},
            backgroundColor: Colors.blue,
            child: const Text('5')),
      ),
    ),
    SizedBox(width: 10, height: 10,), // 여백을 만들기 위해서 넣음.
    SizedBox(
      width: 80.0,
      height: 80.0,
      child: FittedBox(
        child: FloatingActionButton(
            onPressed: () {},
            backgroundColor: Colors.pink,
            child: const Icon(Icons.remove)),
      ),
    ),
  ],
),

 

floatingActionButton IconButton 적용 예제

floatingActionButton: Row(
  mainAxisAlignment: MainAxisAlignment.end,
  children: [
    SizedBox(
      width: 30.0,
      height: 30.0,
      child: FittedBox(
        child: IconButton(onPressed: () {}, icon: const Icon(Icons.add)),
      ),
    ),
    SizedBox(
      width: 10,
      height: 10,
    ), // 여백을 만들기 위해서 넣음.
    Text(
      '5',
      style: TextStyle(
        fontSize: 18,
        fontWeight: FontWeight.w500,
        fontStyle: FontStyle.italic,
        color: Colors.black,
      ),
    ),
    SizedBox(
      width: 10,
      height: 10,
    ), // 여백을 만들기 위해서 넣음.
    SizedBox(
      width: 60.0,
      height: 60.0,
      child: FittedBox(
        child: IconButton(
            onPressed: () {},
            color: Colors.red,
            icon: const Icon(Icons.remove)),
      ),
    ),
  ],
),
 

 

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

Flutter Stack  (0) 2023.12.27
Flutter Custom Button  (0) 2022.07.25
Flutter Column 위젯  (0) 2022.06.24
Flutter Container 위젯  (0) 2022.06.24
Flutter CustomScrollView  (0) 2022.06.21
블로그 이미지

Link2Me

,
728x90

수직방향으로 위젯들을 나란히 배치하는 위젯이다.

Layout은 대부분 Column 과 Row를 조합하여 만들기 때문에 매우 자주 사용된다.

children 프로퍼티에는 여러 위젯의 리스트를 지정할 수 있다.

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Column(
        children: [
          Container(
            color: Colors.red,
            width: 100,
            height: 100,
            padding: const EdgeInsets.all(8.0),
            margin: const EdgeInsets.all(8.0),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                const Text(
                  'Container',
                ),
              ],
            ),
          ),
          Container(
            color: Colors.green,
            width: 100,
            height: 100,
            padding: const EdgeInsets.all(8.0),
            margin: const EdgeInsets.all(8.0),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                const Text(
                  'Container',
                ),
              ],
            ),
          ),
          Container(
            color: Colors.blue,
            width: 100,
            height: 100,
            padding: const EdgeInsets.all(8.0),
            margin: const EdgeInsets.all(8.0),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                const Text(
                  'Container',
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

 

 

 

 

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

Flutter Custom Button  (0) 2022.07.25
Flutter Row 위젯  (0) 2022.06.24
Flutter Container 위젯  (0) 2022.06.24
Flutter CustomScrollView  (0) 2022.06.21
Flutter Drawer Widget  (0) 2022.06.16
블로그 이미지

Link2Me

,
728x90

아무것도 없는 위젯이다. 다양한 프로퍼티를 가지고 있기 때문에 사용하기에 따라서 다양한 응용이 가능하다.

색, 가로와 세로 길이, padding, margin 등의 설정이 가능하고 child 프로퍼티로 또다른 위젯을 자식으로 가질 수 있다.

import 'package:flutter/material.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: 'Container Widget'),
    );
  }
}
 
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) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Container(
        color: Colors.red,
        width: 100,
        height: 100,
        padding: const EdgeInsets.all(8.0),
        margin: const EdgeInsets.all(8.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              'Container',
            ),
          ],
        ),
      ),
    );
  }
}

 

Container 위젯 부분만 발췌한 코드이다.

Container(
  color: Colors.red,
  width: 100,
  height: 100,
  padding: const EdgeInsets.all(8.0),
  margin: const EdgeInsets.all(8.0),
  child: Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
      const Text(
        'Container',
      ),
    ],
  ),
),

 

 

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

Flutter Row 위젯  (0) 2022.06.24
Flutter Column 위젯  (0) 2022.06.24
Flutter CustomScrollView  (0) 2022.06.21
Flutter Drawer Widget  (0) 2022.06.16
Flutter Layout  (0) 2021.12.26
블로그 이미지

Link2Me

,
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

관리자 권한으로 신청해야만 설치가 되더라.

https://dart.dev/get-dart 를 참조해서 설치하면 된다.

 

 

 

 

기본 설치 폴더는 C:\tools\dart-sdk 에 설치된다.

https://rrtutors.com/tutorials/flutter-user-registration-and-login-with-php-mysql 에서 예제 샘플 파일을 받아서 실행했다.

 

 

Flutter Mobile app,Flutter widgets,Jetpack Compose Tutorial,Create Android App,Java,python,Ruby

Create Flutter applications,Jetpack Compose Tutorial,Create android application,Python,Java,RxJava,dart,GoLang examples

rrtutors.com

 

 

 

 

 

 

 

실행했더니 에러가 발생하여

flutter channel stable
flutter upgrade

를 하고 나서 다시 실행했더니 정상적으로 컴파일이 된다.

 

 

화면까지는 정상적으로 잘 나오는데 실제 데이터통신 부분은 소스코드를 살펴보고 해결해야 한다.

'Flutter 앱 > 환경설정' 카테고리의 다른 글

IntelliJ IDEA Community 에 Dart 설치  (0) 2022.06.24
Flutter Upgrade  (0) 2022.06.17
Target of URI doesn't exist 'package:flutter/material.dart'  (0) 2022.01.11
Firebase 프로젝트 등록 방법  (0) 2021.12.29
Flutter 시운전  (0) 2021.12.22
블로그 이미지

Link2Me

,
728x90

구글에서 프로젝트 하나를 다운로드 받아서 실행하려고 시도했더니 이런 메시지가 나온다.

 

터미너를 열고 flutter pub get 를 해준다.

 

'Flutter 앱 > 환경설정' 카테고리의 다른 글

Flutter Upgrade  (0) 2022.06.17
dart-sdk 설치  (0) 2022.01.11
Firebase 프로젝트 등록 방법  (0) 2021.12.29
Flutter 시운전  (0) 2021.12.22
Flutter 설치  (0) 2021.12.20
블로그 이미지

Link2Me

,