1. 리스트뷰 vs 리스트뷰빌더

  - 리스트뷰와 리스트뷰빌더가 있는데 리스트뷰는 모든 리스트를 한 번에 불러오기 때문에 로딩이슈가 발생 할 수 있다.

  - 리스트뷰빌더는 필요한 것만 그때 그때 불러서 사용

2. 리스트뷰빌더예제

  - 리스트뷰형식에 터치를 하면 상세화면이 나옴

  - 주요기능은

    1) 리스트뷰 구현 - 리스트뷰빌더사용하였고 터치를 위해 GestureDetector사용

    2) 팝업창(상세화면) 구현 - showpopup이라는 함수를 만들어서 구현

    3)가로 세로윈도우가 바뀌었을 때도 반응형으로 동작 - description을 구현할 때 mediaquery 클래스 사용

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 const MaterialApp(
      home: ListViewPage(),
    );
  }
}

class ListViewPage extends StatefulWidget {
  const ListViewPage({Key? key}) : super(key: key);

  @override
  State<ListViewPage> createState() => _ListViewPageState();
}

class _ListViewPageState extends State<ListViewPage> {
  var titleList = [
    'Dentist',
    'Pharmacist',
    'School teacher',
    'IT manager',
    'Account',
    'Lawyer',
    'Hairdresser',
    'Physician',
    'Web developer',
    'Medical Secretary'
  ];

  var imageList = [
    'image/1.png',
    'image/2.png',
    'image/3.png',
    'image/4.png',
    'image/5.png',
    'image/6.png',
    'image/7.png',
    'image/8.png',
    'image/9.png',
    'image/10.png'
  ];

  var description = [
    '1.There are different types of careers you can pursue in your life. Which one will it be?',
    '2.There are different types of careers you can pursue in your life. Which one will it be?',
    '3.There are different types of careers you can pursue in your life. Which one will it be?',
    '4.There are different types of careers you can pursue in your life. Which one will it be?',
    '5.There are different types of careers you can pursue in your life. Which one will it be?',
    '6.There are different types of careers you can pursue in your life. Which one will it be?',
    '7.There are different types of careers you can pursue in your life. Which one will it be?',
    '8.There are different types of careers you can pursue in your life. Which one will it be?',
    '9.There are different types of careers you can pursue in your life. Which one will it be?',
    '10.There are different types of careers you can pursue in your life. Which one will it be?'
  ];

  void showPopup(context, title, image, description) {
    showDialog(
        context: context,
        builder: (context) {
          return Dialog(
            child: Container(
              width: MediaQuery.of(context).size.width * 0.7,
              height: 380,
              decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(10), color: Colors.white),
              child: Column(
                children: [
                  ClipRRect(
                      borderRadius: BorderRadius.circular(10),
                      child: Image.asset(
                        image,
                        width: 200,
                        height: 200,
                      )),
                  const SizedBox(
                    height: 10,
                  ),
                  Text(
                    title,
                    style: const TextStyle(
                      fontSize: 25,
                      fontWeight: FontWeight.bold,
                      color: Colors.grey,
                    ),
                  ),
                  Padding(
                    padding: const EdgeInsets.all(8),
                    child: Text(
                      description,
                      maxLines: 3, //최대 3줄까지 표현
                      style: TextStyle(fontSize: 15, color: Colors.grey[500]),
                      textAlign: TextAlign.center,
                    ),
                  ),
                  ElevatedButton.icon(
                      onPressed: () {
                        Navigator.pop(context);
                      },
                      icon: const Icon(Icons.close),
                      label: const Text('close')),
                ],
              ),
            ),
          );
        });
  }

  @override
  Widget build(BuildContext context) {
    double width =
        MediaQuery.of(context).size.width * 0.6; //반응형을 위해 디스크립션을 60%크기로
    return Scaffold(
      appBar: AppBar(
        title: const Text(
          'ListView',
          style: TextStyle(color: Colors.grey),
        ),
        backgroundColor: Colors.white,
        elevation: 0,
      ),
      body: ListView.builder(
        itemCount: titleList.length,
        itemBuilder: (context, index) {
          return GestureDetector(  //InkWell 도 사용가능
            onTap: () {
              debugPrint(titleList[index]);
              showPopup(context, titleList[index], imageList[index],
                  description[index]);
            },
            child: Card(
                child: Row(children: [
              SizedBox(
                width: 100,
                height: 100,
                child: Image.asset(imageList[index]),
              ),
              Padding(
                  padding: const EdgeInsets.all(10),
                  child: Column(
                    children: [
                      Text(titleList[index],
                          style: const TextStyle(
                              fontSize: 22,
                              fontWeight: FontWeight.bold,
                              color: Colors.grey)),
                      const SizedBox(
                        height: 10,
                      ),
                      SizedBox(
                          width: width,
                          child: Text(description[index],
                              style: TextStyle(
                                fontSize: 15,
                                color: Colors.grey[500],
                              )))
                    ],
                  ))
            ])),
          );
        },
      ),
    );
  }
}

 

 

온보딩 스크린은 앱을 처음 설치했을 때 간단한 안내사항 및 튜토리얼 같은 것을 안내하는 페이지

 

1. 구현을 위해 다음 패키지를 설치해야한다

introduction_screen: ^3.1.2

https://pub.dev/packages/introduction_screen

 

introduction_screen | Flutter Package

Introduction/Onboarding package for flutter app with some customizations possibilities

pub.dev

 

2. 파일은

main.dart (기본 화면) - 앱의 메인화면이 시작 될 것

onboarding.dart (온보딩 스크린 꾸미기)

 

3. main.dart

Elevated버튼 부분에서 push 대신 pushReplacement를 사용했는데 뒤로가기 버튼을 없앨 수 있다.

MaterialApp의 홈부분에서 MyPage로 가는게 아니라 OnBoardingPage로 연결되는 것이 포인트

(사실 온보딩페이지는 1번만 보여주고 그 다음부터는 보여주면 안되는 것이지만 우리는 연습을 위해서 이렇게 함)

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

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: OnBoardingPage(),
    );
  }
}

class MyPage extends StatelessWidget {
  const MyPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Main page'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              'Main Screen',
              style: TextStyle(fontWeight: FontWeight.bold, fontSize: 25),
            ),
            ElevatedButton(
                onPressed: () {
                  Navigator.of(context).pushReplacement(
                      MaterialPageRoute(builder: (context) => const OnBoardingPage()));
                },
                child: const Text('Go to onboarding Screen'))
          ],
        ),
      ),
    );
  }
}

 

4. onboarding.dart

우선 IntroductionScreen에 보면 아주 다양한 속성들을 가지고 있는데 여기서 우리는 아래 속성들을 편집할 예정

page: 배치할 페이지 꾸미는 부분

done: 마지막 화면에 띄워줄 글씨

onDone: 마지막 화면에서 메인 페이지로 연결

next: 다음 화면 표시할 것 (우리는 forward 아이콘 사용)

showSkipButton (skip버튼 사용 여부)

skip: 화면에 표시할 것

dotsDecoration: 화면 진행상황 표시 점을 꾸미기

curve: 에니메이션 효과 넣기

 

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

import 'main.dart';

class OnBoardingPage extends StatelessWidget {
  const OnBoardingPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return IntroductionScreen(
      pages: [
        PageViewModel(
            title: 'Welcome to my app',
            body: 'This is the first Page',
            image: Image.asset('image/page1.png'),
            decoration: getPageDecoration()),
        PageViewModel(
            title: 'Welcome to my app',
            body: 'This is the second Page',
            image: Image.asset('image/page2.png'),
            decoration: getPageDecoration()),
        PageViewModel(
            title: 'Welcome to my app',
            body: 'This is the third Page',
            image: Image.asset('image/page3.png'),
            decoration: getPageDecoration()),
      ],
      done: const Text('Done!'),
      onDone: () {
        Navigator.of(context)
            .pushReplacement(MaterialPageRoute(builder: (context) => const MyPage()));
      },
      next: const Icon(Icons.arrow_forward),
      showSkipButton: true,
      skip: const Text("Skip"),
      dotsDecorator: DotsDecorator(
          color: Colors.cyan,
          size: const Size(10, 10),
          activeSize: const Size(22, 10),
          activeShape:
              RoundedRectangleBorder(borderRadius: BorderRadius.circular(24))),
      curve: Curves.bounceOut,
    );
  }
}

PageDecoration getPageDecoration() {
  return const PageDecoration(
    titleTextStyle: TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
    bodyTextStyle: TextStyle(
      fontSize: 18,
      color: Colors.blue,
    ),
    imagePadding: EdgeInsets.only(top: 40),
    pageColor: Colors.orange,
  );
}

 

페이지별로 동일한 데코레이션을 모든 페이지마다 따로 코드를 작성한다면 너무 비효율적임

그래서 우리는 pageDecoration메서드를 만들어서 decoration을 함수 호출방식으로 사용

반복작업을 효과적으로 바꿈

route는 하나의 페이지라고 생각하면 된다.

이번에는 다른 파일의 페이지를 여는 방식을 살펴볼 것이다. Navigator1에서 다룬 내용은 싱글 페이지에서 페이지 연결하는 방식? 이라 생각됨(아직은 잘 몰라서 ..)

MaterialApp에서 home을 사용하는게 아니라 initialRoute와 routes를 이용해서 페이지 구조를 연결함

Navigator의 pushNamed를 사용

즉, home과는 다른 방식

4개의 파일 구조는 아래와 같다.

 

main. dart

import 'package:flutter/material.dart';
import 'package:flutter_1229/ScreenA.dart';
import 'package:flutter_1229/ScreenB.dart';
import 'package:flutter_1229/ScreenC.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute:'/',
      routes: {
        '/' : (context) => ScreenA(),
        '/b' : (context) => ScreenB(),
        '/c' : (context) => ScreenC(),
      },
    );
  }
}

 

 

ScreenA.dart

import 'package:flutter/material.dart';

class ScreenA extends StatelessWidget {
  const ScreenA({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('ScreenA')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
                style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
                onPressed: () {
                  Navigator.pushNamed(context, '/b');
                },
                child: Text('Go to ScreenB')),
            ElevatedButton(
                style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
                onPressed: () {
                  Navigator.pushNamed(context, '/c');
                },
                child: Text('Go to ScreenC'))
          ],
        ),
      ),
    );
  }
}

 

ScreenB.dart

import 'package:flutter/material.dart';

class ScreenB extends StatelessWidget {
  const ScreenB({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("ScreenB"),
        ),
        body: Center(
          child: Text(
            'ScreenB',
            style: TextStyle(fontSize: 24),
          ),
        ));
  }
}

 

ScreenC.dart

import 'package:flutter/material.dart';

class ScreenC extends StatelessWidget {
  const ScreenC({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("ScreenC"),
        ),
        body: Center(
          child: Text(
            'ScreenC',
            style: TextStyle(fontSize: 24),
          ),
        ));
  }
}

 

 

 

 

 

 

* 우선 22~23강의 route개념은 중요하므로 여러번 봐야할 듯 , 그리고 24강 끝에 해답이있는데 이것이 핵심? ㅜㅜ

1. Route는 페이지라 생각 하면 됨

2. Navigator는 push, pop을 사용하며 stack형태를 이룸

3. push할 때 MaterialPageRoute(builder)를 사용함 *

  - BuildContext정보가 중요함

** 매우 중요한 것 MaterialPageRoute에서 context 인자로 넘겨줄 때 MaterialApp의 상위 위젯의 context를 넘겨주면 안된다. 그렇기에 아래 push처럼 MaterialApp의 하위 child의 context를 넘겨줘야함.

(아래 코드들에서 첫 route는 First이고 두번째 route는 Second임)

push 핵심 코드

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('First'),
      centerTitle: true,
    ),
    body: Center(
      child: ElevatedButton(
        child: Text('Go to the Second Page'),
      onPressed: () {
        Navigator.push(context,
          MaterialPageRoute(builder: (BuildContext context) {
          return Second();
        }));
      },
    )),
  );
}

만약 builder의 매개변수를 사용하지 않을 경우 아래처럼 _ 를 사용하는 것을 추천함

onPressed: () {
  Navigator.push(context,
    MaterialPageRoute(builder: (_) => Second()
  ));
},

 

pop 핵심 코드

Widget build(BuildContext context2) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Second'),
      centerTitle: true,
    ),
    body: Center(
        child: ElevatedButton(
      child: Text('Go to the First Page'),
      onPressed: () {
        Navigator.pop(context2);
      },
    )),
  );
}

 

 

전체 코드

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Toast Message',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: First(),
    );
  }
}

class First extends StatelessWidget {
  const First({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First'),
        centerTitle: true,
      ),
      body: Center(
          child: ElevatedButton(
        child: Text('Go to the Second Page'),
        onPressed: () {
          Navigator.push(context,
            MaterialPageRoute(builder: (BuildContext context) {
            return Second();
          }));
        },
      )),
    );
  }
}

class Second extends StatelessWidget {
  const Second({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context2) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Second'),
        centerTitle: true,
      ),
      body: Center(
          child: ElevatedButton(
        child: Text('Go to the First Page'),
        onPressed: () {
          Navigator.pop(context2);
        },
      )),
    );
  }
}

 

추천 사이트: Flutter Layout Cheat Sheet. Do you need simple layout samples for… | by Tomek Polański | Flutter Community | Medium

1. Column

- 아래처럼 새로로 배치할 때 사용하며, 중요한 건 컬럼은 아래 공간까지 다 차지한다는 것!

- 정렬방법1. Center위젯으로 감싸면 아래처럼 되고

- 정렬방법2. Column에 있는 위젯들을 세로로 정렬할 때는 아래와 같은 속성을 사용할 수 있다.

mainAxisAlignment: MainAxisAlignment.spaceEvenly

- 정렬방법3. 조금 중요한데 요소들을 오른쪽 끝에 붙이고 싶으면?

crossAxisAlignment: CrossAxisAlignment.end,

위와 같이 명령어를 써도 실제로는 동작하지 않는다.. 왜그럴까? 일단 동작하는 건 맞다. 하지만 가로 폭이 요소폭이랑 같아서 변화가 없는 것

아래와 같은 invinsible Container를 사용하면 된다.

Container(
  width: double.infinity,
)

 

 

 

 

 

 

 

 

 

사용 결과

 

- 정렬방법4. 컨테이너를 가로로 꽉 채우고 싶으면? 아래와 같이 주고 각 컨테이너의 폭width를 다 없애준다.

crossAxisAlignment: CrossAxisAlignment.stretch,

- 정렬방법5. 박스간의 간격을 조정하고 싶을 때는 SizedBox를 넣어서 간격 조절하기

 

 

2. Row는 Column과 반대 개념

 

뭐가 문제인지 모르겠지만.. 나오긴하나 모양이 안바뀜 ㅜㅜ 텍스트는 잘 바뀐다.. 이유는 천천히 찾아보자.

 

우선 fluttertoast는 pub에서 패키지를 받아서 구현할 수 있다.

fluttertoast | Flutter Package (pub.dev)

 

fluttertoast | Flutter Package

Toast Library for Flutter, Easily create toast messages in single line of code

pub.dev

pubspec.yaml의 flutter에 패키지를 등록해주면 import가 가능하다

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

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Toast Message',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyPage(),
    );
  }
}

class MyPage extends StatelessWidget {
  const MyPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Toast Message'),
        centerTitle: true,
      ),
      body: Center(
          child: ElevatedButton(
              onPressed: () {
                flutterToast();
              },
              child: Text('Toast'),
              style: TextButton.styleFrom(
                backgroundColor: Colors.greenAccent
              ))),
    );
  }
}

void flutterToast() {
  Fluttertoast.showToast(
      msg: "This is Center Short Toast",
      toastLength: Toast.LENGTH_SHORT,
      gravity: ToastGravity.CENTER,
      timeInSecForIosWeb: 1,
      backgroundColor: Colors.red,
      textColor: Colors.white,
      fontSize: 16.0
  );
}

 

 

BuildContext개념은 강좌 확인

 

변경된 사용법

onPressed: () {
  ScaffoldMessenger.of(context)
      .showSnackBar(SnackBar(content: Text('Hello')));
}),

참고

https://yj95.tistory.com/364

 

import 'package:flutter/material.dart';

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

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: MyPage(),
    );
  }
}

class MyPage extends StatelessWidget {
  const MyPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('Snack Bar'),
          centerTitle: true,
        ),
        body: Center(
          child: TextButton(
              child: Text(
                'show Me',
                style: TextStyle(
                  color: Colors.white,
                  backgroundColor: Colors.red,
                ),
              ),
              onPressed: () {
                ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                    backgroundColor: Colors.teal,
                    duration: Duration(milliseconds: 1000),
                    content: Text(
                      'Hello',
                      textAlign: TextAlign.center,
                      style: TextStyle(color: Colors.white),
                    )));
              }),
        ));
  }
}

App바 기본

1. leading : 아이콘 버튼이나 간단한 위젯을 왼쪽에 배치할 때

2. actions : 복수의 아이콘 버튼 등을 오른쪽에 배치했을 때

3. onPressed : 함수형태로 버튼이나 아이콘 터치했을 때 발생하는 이벤트를 정의하는 곳

 

코드

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'AppBar',
      theme: ThemeData(
        primarySwatch: Colors.red,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Appbar icon Menu'),
        centerTitle: true,
        elevation: 0.0,
        leading: IconButton(
          icon: Icon(Icons.menu), onPressed: () {
            print('menu button is clicked');
          },
        ),
        actions: [
          IconButton(
            icon: Icon(Icons.shopping_cart), onPressed: () {
            print('shp cart button is clicked');
          },
          ),
          IconButton(
            icon: Icon(Icons.search), onPressed: () {
            print('search button is clicked');
          },
          ),
        ],
      ),
    );
  }
}

 

drawer메뉴

 

기본 구성

 

 

* onTab, onPressed 차이

 

onPressed는 버튼에 주로 사용

 

onTab은 gestureDetector, InkWell 같은데 주로 사용

  - 길게 누르기, 두번 탭하기 등 액션 감지 가능

 

drawer코드

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'AppBar',
      theme: ThemeData(
        primarySwatch: Colors.red,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Appbar icon Menu'),
        centerTitle: true,
        elevation: 0.0,
        actions: [
          IconButton(
            icon: Icon(Icons.shopping_cart), onPressed: () {
            print('shp cart button is clicked');
          },
          ),
          IconButton(
            icon: Icon(Icons.search), onPressed: () {
            print('search button is clicked');
          },
          ),
        ],
      ),
      drawer: Drawer(
        child: ListView(
          padding: EdgeInsets.zero,
          children: [
            UserAccountsDrawerHeader(
              currentAccountPicture: CircleAvatar(
                backgroundImage: AssetImage('1.png'),
                backgroundColor: Colors.white,
              ),
              otherAccountsPictures: [
                CircleAvatar(
                  backgroundColor: Colors.white,
                  backgroundImage: AssetImage('1.png'),
                ),
                // CircleAvatar(
                //   backgroundColor: Colors.white,
                //   backgroundImage: AssetImage('1.png'),
                // ),
              ],
              accountName: Text('changPa'),
              accountEmail: Text('changPa@chang.com'),
              onDetailsPressed: (){
                print('arrow is clicked');
              },
              decoration: BoxDecoration(
                color: Colors.red[200],
                borderRadius: BorderRadius.only(
                  bottomLeft: Radius.circular(20.0),
                  bottomRight: Radius.circular(20.0),
                )
              ),
            ),
            ListTile(
              leading: Icon(Icons.home,
              color: Colors.grey[850]),
              title: Text('Home'),
              onTap: (){
                print('Home is clicked');
              },
              trailing: Icon(Icons.add),
            ),
            ListTile(
              leading: Icon(Icons.settings,
              color: Colors.grey[850]),
              title: Text('Settings'),
              onTap: (){
                print('Settings is clicked');
              },
              trailing: Icon(Icons.add),
            ),
            ListTile(
              leading: Icon(Icons.question_answer,
              color: Colors.grey[850]),
              title: Text('Q&A'),
              onTap: (){
                print('Q&A is clicked');
              },
              trailing: Icon(Icons.add),
            ),

          ],
        ),
      ),
    );
  }
}

 

- 모든 변수는 기본적으로 non-nullable임, 즉 null을 할당 할 수 없음

- 모든 변수는 non-nullable이므로 null check가 필요없음(과거 if문으로 null일 경우 이렇게 해라 등)

- class 내의 변수 역시 non-nullable이므로 초기화가 필요하다.

 

 

1. Nullable type

 - Null이 올 수도 있다.

 - type뒤에 ? 표기

 

2. late 키워드

 - 잠시 후 초기화 할 거다.

 - type 앞에 late 표기

 

3. Exclamation or Bang   - !

 - 원래 Nullable변수는 non-Nullable변수에 대입할 수 없지만 이 변수는 절대 null 값을 가지지 않는 다는 것을 명시해서 대입가능하게 함

 - 대입하려는 변수 뒤에 ! 표기

 

4. named argument 형식에서 사용 (우리가 사용하는 Scaffold, Center등의 클래스가 named argument형식)

void main(){
  print(add(a:4, b:2));
}

int add({required int a, required int b}){
  int sum = a+b;
  return sum;
}

named argument는 선택적이라 null이 할당될 우려가 있음.

그래서 required 키워드를 붙여서 무조건 매개변수 값을 넘겨주도록 하거나

아니면 Nullable Type( ? )으로 변수를 선언하는 방법으로 회피하는데 함수 내에서 여전히 null값으로 남게되면 if문을 사용해서 null체크하라는 에러메시지를 띄운다.

 

5. lazy initialization (late 키워드 보충 - late 변수는 언제 초기화 되나?) 

class Person{
  late int age = calculation();
  void printAge(){
    print('age');
  }
}

int calculation(){
  print('calculate');
  return 30;
}

void main(){
  Person p = Person();
  p.printAge();
  print(p.age);
}

만약 age변수가 late키워드가 없었다면 출력은

calculate -> age -> 30으로 출력 될 것이다.

하지만 late키워드가 있기 때문에

age -> calculate -> 30 순으로 출력된다.

여기서 우리는 late키워드가 있으면 해당 변수(여기서는 age)가 참조될 때 실행된다는 것을 알 수 있다.

 

6. 이전버전 null-safety버전으로 마이그레이팅하기

https://www.youtube.com/watch?v=SjJ6pxYuqwg&list=PLQt_pzi-LLfpx8x6YEMvUwfJHZIEk2L6J&index=3

 

출처: https://www.youtube.com/watch?v=QP0THWoDeag&list=PLQt_pzi-LLfpx8x6YEMvUwfJHZIEk2L6J&index=2

[완성 화면]

[주요 개념]

- 이미지 import

- 위젯간의 간격은 Sizedbox로 조정

- padding

padding: EdgeInsets.fromLTRB(30, 40, 0, 0)

- Debug표시 없애기

debugShowCheckedModeBanner: false,

- 써클 아바타

CircleAvatar(
  backgroundImage: AssetImage('assets/charizard-mega-y.png'),
  radius: 60,
  backgroundColor: Colors.amber[600],
)

 

[완성 전체 코드]

import 'package:flutter/material.dart';
import 'package:mild_5/1.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'BBANTO',
      home: Grade()
    );
  }
}

class Grade extends StatelessWidget {
  const Grade({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.amber[600],
      appBar: AppBar(
        title: Text('BBANTO'),
        backgroundColor: Colors.amber[700],
        centerTitle: true,
        elevation: 0,
      ),
      body: Padding(
        padding: EdgeInsets.fromLTRB(30, 40, 0, 0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Center(
              child: CircleAvatar(
                backgroundImage: AssetImage('assets/charizard.gif'),
                radius: 60.0,
                backgroundColor: Colors.black,
              ),
            ),
            Divider(
              height: 60.0, // 가로선 기준 위아래 간격이 30씩
              color: Colors.grey[850],
              thickness: 1,
              endIndent: 30.0,
            ),
            Text('Name',
            style: TextStyle(
              color: Colors.white,
              letterSpacing: 2.0,
            ),
            ),
            SizedBox(
              height: 10.0,
            ),
            Text('BBANTO',
            style: TextStyle(
              color: Colors.white,
              letterSpacing: 2,
              fontSize: 28,
              fontWeight: FontWeight.bold
            ),),
            SizedBox(
              height: 30,
            ),
            Text('BBANTO POWER LEVEL',
              style: TextStyle(
                color: Colors.white,
                letterSpacing: 2.0,
              ),
            ),
            SizedBox(
              height: 10.0,
            ),
            Text('14',
              style: TextStyle(
                  color: Colors.white,
                  letterSpacing: 2,
                  fontSize: 28,
                  fontWeight: FontWeight.bold
              ),),
            SizedBox(
              height:25
            ),
            Row(
              children: [
                Icon(Icons.check_circle_outline),
                SizedBox(
                  width: 10,
                ),
                Text('using lighsaber',
                  style: TextStyle(
                    fontSize: 16,
                    letterSpacing: 1
                  ),
                )
              ],
            ),
            Row(
              children: [
                Icon(Icons.check_circle_outline),
                SizedBox(
                  width: 10,
                ),
                Text('face hero tattoo',
                  style: TextStyle(
                      fontSize: 16,
                      letterSpacing: 1
                  ),
                )
              ],
            ),
            Row(
              children: [
                Icon(Icons.check_circle_outline),
                SizedBox(
                  width: 10,
                ),
                Text('fire flames',
                  style: TextStyle(
                      fontSize: 16,
                      letterSpacing: 1
                  ),
                )
              ],
            ),
            Center(
              child: CircleAvatar(
                backgroundImage: AssetImage('assets/charizard-mega-y.png'),
                radius: 60,
                backgroundColor: Colors.amber[600],
              )
            )
          ],
        ),
      ),

    );
  }
}

 

pubspec.yaml 파일 띄워쓰기 2칸 중요

아래와 같이 사용

 

 

 

import 'package:flutter/material.dart'; //flutter/material.dart 불러오기

void main() => runApp(MyApp());  //최상단 MyApp실행하기

class MyApp extends StatelessWidget {  //MyApp 설계
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(  //MyApp에서 가장 기본인 MaterialApp 설정하기
      title:'My First App', //보통 타이틀, 테마, 홈으로 구성
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),  // home이 중요한데 가장 먼저 열리는 페이지 실행
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(  //페이지에서 가장 기본인 Scaffold로 시작
        appBar: AppBar( //scaffold의 구성은 appBar와 body가 기본
          title: Text('My App'),
        ),
        body: Center( //body는 보통 center에 column넣고 시작
          child: Column(
            children: [
              Text("Hello"),
              Text("Hello"),
              Text("Hello"),
              Text("Hello"),
            ],
          ),
        )
    );
  }
}

 

+ Recent posts