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으로 가기'))
],
),
);
}),
),
);
}
}
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"),
),
],
)
],
),
),
);
}
}
- buttonBar 의 사용 예시
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;
}
🔶 🔷 📌 🔑
Reference
-
SnackBars managed by the ScaffoldMessenger - https://flutter.dev/docs/release/breaking-changes/scaffold-messenger
-
Null safety in Flutter - https://flutter.dev/docs/null-safety
-
코딩 셰프 - https://www.youtube.com/watch?v=0LNUSnmzDg4&list=PLQt_pzi-LLfpx8x6YEMvUwfJHZIEk2L6J&index=1
Leave a comment