Flutter 2.0 with null safety

Updated:

1.ScaffoldMessenger class

  • Manages SnackBars for descendant Scaffolds

  • By default, a root ScaffoldMessenger is included in the MaterialApp. : route 가 바뀌어도 언제든지 snackBar 를 호출해서 사용할 수 있게 됩니다

  • The ScaffoldMessenger now handles SnackBars in order to persist across routes and always be displayed on the current Scaffold. : 최상의 root 에서 관리 되기때문에 언제든지 현재 Scaffold 에서 가져다가 사용할 수 있다는 것입니다.

  • flutter 2.0 부터 지원되는 ScaffoldMessenger class 는 여러가지 흩어져 있는 scaffold 에 각 snackBar 를 손쉽게 전달해 줄 수 있는 widget 입니다.

// in main.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(primaryColor: Colors.lightBlue),
      // 초기 화면의 경로를 표시해줌
      initialRoute: '/',
      // named routes 를 map 형식으로 아래와 같이 경로를 지정해 준다
      routes: {
        '/': (context) => ScreenA(),
        '/b': (context) => ScreenB(),
        '/c': (context) => ScreenC(),
      },
    );
  }
}
// in screen_a.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: ElevatedButton(
          child: Text('Go to the Second Page'),
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => ScreenB()),
            );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.thumb_up),
        onPressed: () {
          // scaffoldMessenger 를 통해 다른 페이지로 이동을 하더라도 5초동안 계속 snackBar 가 나타나게 됩니다 => 왜냐하면 모든 자손 scaffold 가 등록이 되어있기 때문에 snackBar 를 공유해서 계속 사용할 수 있게 됩니다.
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(
              content: Text('좋아요가 추가 되었습니다. 감사합니다'),
              duration: Duration(seconds: 5),
              action: SnackBarAction(
                label: 'Undo',
                onPressed: () {
                  Navigator.pushNamed(context, '/c');
                },
              ),
            ),
          );
        },
      ),
    );
  }
}
// in screen_b.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: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              '"좋아요가 추가 되었습니다',
              style: TextStyle(
                fontSize: 20.0,
                color: Colors.blue,
              ),
            ),
            SizedBox(
              height: 40.0,
            ),
            ElevatedButton(
              onPressed: () {
                Navigator.pop(context);
              },
              child: Text('Home 으로 이동'),
            ),
          ],
        ),
      ),
    );
  }
}
// in screen_c.dart

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

  @override
  Widget build(BuildContext context) {
    // 2.0 이전방식으로 단독으로 snakBar 사용할 경우 개별적인 Scaffold 에서 ScaffoldMessenger 를 사용해서 Builder 를 만들어서 개별 적용해주면 됨
    return ScaffoldMessenger(
      child: Scaffold(
        appBar: AppBar(
          title: Text('ScreenC'),
        ),
        body: Builder(builder: (context) {
          return Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(
                  '좋아요를 취소 하시겠습니까?',
                  style: TextStyle(fontSize: 24.0),
                ),
                SizedBox(
                  height: 50.0,
                ),
                ElevatedButton(
                  onPressed: () {
                    ScaffoldMessenger.of(context).showSnackBar(
                      SnackBar(
                        content: Text('"좋아요" 가 취소 되었습니다.'),
                        duration: Duration(seconds: 3),
                        action: SnackBarAction(
                          label: "Home으로 이동",
                          onPressed: () {
                            Navigator.pop(context);
                          },
                        ),
                      ),
                    );
                  },
                  child: Text('취소 하기'),
                ),
                SizedBox(
                  height: 50.0,
                ),
                ElevatedButton(
                    onPressed: () {
                      Navigator.pop(context);
                    },
                    child: Text('Home으로 가기'))
              ],
            ),
          );
        }),
      ),
    );
  }
}

Kapture 2021-10-14 at 17 07 40

2.Buttons (TextButton, ElevatedButton, OutlinedButton)

  • 2.0 에서 새로 추가, 변경된 button 의 속성 및 icon 과 같이 사용되는 button 들은 아래의 코드와 같습니다.
class ScreenA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Buttons'),
        centerTitle: true,
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // Text button
            TextButton(
              onPressed: () {
                print('text button');
              },
              // 길게 눌렀을때 실행할 함수
              onLongPress: () {
                print('text button');
              },
              child: Text(
                'Text button',
                style: TextStyle(fontSize: 20.0),
              ),
              // TextButton 의 스타일
              style: TextButton.styleFrom(
                primary: Colors.red,
                //backgroundColor: Colors.blue
              ),
            ),

            // ElevatedButton
            ElevatedButton(
              onPressed: () {
                print('Elevated button');
              },
              child: Text('Elevated button'),
              style: ElevatedButton.styleFrom(
                  primary: Colors.orangeAccent,
                  shape: RoundedRectangleBorder(
                      borderRadius: BorderRadius.circular(10.0)),
                  elevation: 0.0),
            ),

            // OutlinedButton
            OutlinedButton(
              onPressed: () {
                print('Outlined button');
              },
              child: Text('Outlined button'),
              style: OutlinedButton.styleFrom(
                primary: Colors.green,
                // side: 에서 Border line 을 설정 할 수 있음
                side: BorderSide(color: Colors.black87, width: 2.0),
              ),
            ),

            // TextButton with icon
            TextButton.icon(
              onPressed: () {
                print('Icon button');
              },
              // 만약 icon 의 색을 지정하지 않으면 기본 Primary 색으로 설정됨
              icon: Icon(
                Icons.home,
                size: 30.0,
                color: Colors.black87,
              ),
              label: Text('Go to home'),
              style: TextButton.styleFrom(
                primary: Colors.purple,
              ),
            ),

            // ElevatedButton with icon
            ElevatedButton.icon(
              onPressed: () {
                print('Go to Home');
              },
              icon: Icon(
                Icons.home,
                size: 20,
              ),
              label: Text('Go to Home'),
              style: ElevatedButton.styleFrom(
                  primary: Colors.black, minimumSize: Size(200, 50)),
            ),

            // OutlinedButton with icon
            OutlinedButton.icon(
              onPressed: () {
                print('Outlined icon button');
              },
              icon: Icon(Icons.home),
              label: Text('Go to home'),
              style: OutlinedButton.styleFrom(
                primary: Colors.black,
              ),
            ),

            // ElevatedButton with icon
            ElevatedButton.icon(
              onPressed: null,
              icon: Icon(
                Icons.home,
                size: 20,
              ),
              label: Text('Go to Home'),
              style: ElevatedButton.styleFrom(
                primary: Colors.black,
                onSurface: Colors.pink,
                minimumSize: Size(200, 50),
              ),
            ),

            // ButtonBar
            // An end-aligned row of buttons, laying out into a column if there is not enough horizontal space.
            ButtonBar(
              alignment: MainAxisAlignment.end,
              buttonPadding: EdgeInsets.all(20),
              children: [
                TextButton(
                  onPressed: () {},
                  child: Text("TextButton"),
                ),
                ElevatedButton(
                  onPressed: () {},
                  child: Text("ElevatedButton"),
                ),
              ],
            )
          ],
        ),
      ),
    );
  }
}

image

  • buttonBar 의 사용 예시

image

3.Null safety

  • Null : 아직 값이 정해 지지 않은것을 말합니다.

int age = 20; 와 같이 변수는 Primitive type (기본 타입) 을 가져야 합니다 => Dart 언어는 type safe language 로 type을 지정해야 합니다

int age = null; 코드의 type 은 int or null type 으로 표현할 수 있는데, 실제 이러한 type 은 존재하지 않습니다

  • 이것을 방지하고자, Null safety 개념이 등장하는데, 변수는 절대 null 값을 가질 수 없다는 것입니다. 즉, 변수를 생성할때 null 이 생성되지 않도록 default 값으로 설정해 놓는 것입니다. 그래서 int age = null 을 더이상 할 수 없게 됩니다. (compile 단계에서 안되게 됨)

Null safety 특징

1.모든 변수는 null이 될 수 없으며, non-nullable 변수에는 null 값을 할당할 수 없습니다

2.non-nullable 변수를 위한 null check 가 필요없습니다

3. “Class 내의 변수는” 반드시 선언과 동시에 초기화를 시켜야 합니다

class Person {
  // null safety 적용 전에는 선언만 하고 변수 사용이 가능하였으나 2.0 이후에는 사용이 안된다
  int age;

  // null safety 적용 이후
  int age = 30;
}

Null safety 수정방법 Case.1

// String 에 변수 선언만하고 value 값이 없는 상태 null 인 경우 이기 때문에, null safety 적용 후 error 발생
class Person {
  // error : Non-nullable instance field 'name' must be initialized.
  String name;

  String nameChange(String name) {
    this.name = name;
    return name.toUpperCase();
  }
}

void main() {
  Person p = Person();
  p.name = 'Jacob';
  print(p.nameChange(p.name));
}
class Person {

  // String or null 의 타입을 지정
  String? name;

  // String type 에 String or null 임
  String nameChange(String? name) {
    this.name = name;
    // type check 하는 null 임
    if(name == null) {
      return 'need a name';
    } else {
     return name.toUpperCase();
    }
  }
}

void main() {
  Person p = Person();
  p.name = 'Jacob';
  print(p.nameChange(p.name));
}

Null safety 수정방법 Case.2

// class 에 age 선언 null => null safety 전에는 정상작동함
class Person {
  // 선언과 동시에 할당되는 것이 아니라, 나중에 할당이 되어져야 하는 경우임
  int age;

  // constructor 생성
  int sum(int age, int num) {
    this.age = age;
    int total = age + num;
    return total + age;
  }
}

void main() {
  // instance
  Person p = Person();
  print(p.sum(100, 50));
}
class Person {
  // 나중에 선언 하겠다는 의미로 late initialized 앞에 late 을 붙이면 null safety 에서 error 가 나타나지 않음
  late int age;

  int sum(int age, int num) {
    this.age = age;
    int total = age + num;
    return total + age;
  }
}

void main() {
  Person p = Person();
  print(p.sum(100, 50));
}

Null safety 수정방법 Case.3

void main() {
  int x = 50;
  int? y;
  if(x > 0) {
    y = x;
  }
  // A value of type "int?" can't be assigned to a variable of type "int" 에러 발생 : nullable type 인 y 에는 int type 인 value 에 할당 할 수 없기 때문에 error 발생시킴
  int value = y;
  print(value);
}
void main() {
  int x = 50;
  int? y;
  if(x > 0) {
    y = x;
  }
  // nullable 변수 y 는 항상 non-nullable 값을 가질것이라고 알려주는 것은 y 뒤에 ! (exclamation or bang mark) 를 붙여서 실행하면 error 가 발생 안됨 즉, Not nullable type 은 ! 로 표시 한다
  int value = y!;
  print(value);
}

Null safety 수정방법 Case.4

void main() {
  print(add());
}

// named argument 는 optional 한거기 때문에 위에 add method 를 추가할때 아무값도 없을때 error 가 발생하지 않음
// int 변수는 null 값을 가질 수 없기 때문임
int add({int a, int b}) {
  int sum = a+ b;
  return sum;
}
void main() {
  print(add(a: 4, b: 5));
}

// named argument 는 optional 이기 때문에 required 를 붙이면 필수 적인 값으로 선언 할 수 있음
int add({required int a, required int b}) {
  int sum = a+ b;
  return sum;
}

Null safety codelab


🔶 🔷 📌 🔑

Reference

Categories:

Updated:

Leave a comment