이전 글에 이어서 코드를 작성하며,
버튼을 클릭했을 때 데이터 전송까지 이루어지도록 하겠다.
이전 코드는 아래의 글을 참고하자.
중복된 코드를 MainLayout이라는 위젯으로 다른 파일에 빼서 관리했다.
[Flutter] 버튼(Button) - ElevatedButton, OutlinedButton, TextButton
1. 변수와 생성자, pop을 통한 데이터 전송
home_screen.dart와 one_screen.dart의 위젯 간의 데이터 전송을 해보도록 하자.
home_screen에서 one_screen으로 데이터를 전송하기 위해 one_screen에 변수와 생성자를 선언한다.
(1) 변수, 생성자 선언
one_screen.dart
final String str;
const OneScreen({required this.str, Key? key}) : super(key: key);
(2) 받아온 데이터 띄워주기
one_screen.dart
Text(
str.toString(),
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.red,
fontSize: 20.0,
),
),
(3) async, await를 사용해 push에서 데이터 넘겨주기
"메인에서 넘어온 데이터"라는 String 값을 넘겨주었다.
home_screen.dart
@override
Widget build(BuildContext context) {
return MainLayout(
title: 'Home Screen',
children: [
ElevatedButton(
onPressed: () async {
final result = await Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => OneScreen(str: '메인에서 넘어온 데이터'),
));
print(result);
},
child: Text('첫번째 페이지 이동'),
),
],
);
}
그럼 아래와 같이 '첫번째 페이지 이동' 버튼을 누르면 다음 화면에 값이 전달되는 것을 확인할 수 있다.
home_screen.dart 코드
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
import 'package:flutter_application_arrange/layout/main_layout.dart';
import 'one_screen.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return MainLayout(
title: 'Home Screen',
children: [
ElevatedButton(
onPressed: () async {
final result = await Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => OneScreen(str: '메인에서 넘어온 데이터'),
));
print(result);
},
child: Text('첫번째 페이지 이동'),
),
],
);
}
}
one_screen.dart 코드
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
import 'package:flutter_application_arrange/layout/main_layout.dart';
class OneScreen extends StatelessWidget {
final String str;
const OneScreen({required this.str, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MainLayout(
title: 'First Screen',
children: [
Text(
str.toString(),
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.red,
fontSize: 20.0,
),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop('pop으로 넘어온 데이터');
},
child: Text('뒤로가기'),
)
],
);
}
}
one_screen에서 pop으로 데이터를 넘겨 주고, home_screen에서 result로 데이터를 받아와 print를 하니 디버그 콘솔에 pop으로 넘겨준 데이터가 출력되는 것을 확인할 수 있다.
2. Argument를 사용한 데이터 전송
1. settings: RouteSettings(arguments:) 사용해 값 넘겨주기
one_screen.dart
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) => TwoScreen(),
settings: RouteSettings(arguments: '전송한 데이터'),
),
);
},
2. ModalRoute.of(context) 사용해서 arguments 값 받아오기
ModalRoute는 FullSceen 위젯인 전체 화면을 의미하는 위젯을 말한다.
! 느낌표는 ModalRoute를 받아올 수 없는 경우도 고려해야 하기 때문에 !를 붙여준다.
final arguments = ModalRoute.of(context)!.settings.arguments;
아래의 화면을 보면 첫번째 페이지 - 두번째 페이지 - 세번째 페이지를 넘나들며 데이터를 받아오는 것을 확인할 수 있다.
one_screen.dart 코드
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
import 'package:flutter_application_arrange/layout/main_layout.dart';
import 'package:flutter_application_arrange/screen/two_screen.dart';
class OneScreen extends StatelessWidget {
final String str;
const OneScreen({required this.str, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MainLayout(
title: 'First Screen',
children: [
Text(
str.toString(),
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.red,
fontSize: 20.0,
),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop('pop으로 데이터');
},
child: Text('뒤로가기'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) => TwoScreen(),
settings: RouteSettings(arguments: '전송한 데이터'),
),
);
},
child: Text("두번째 페이지 이동"),
),
],
);
}
}
two_screen.dart 코드
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
import 'package:flutter_application_arrange/layout/main_layout.dart';
class TwoScreen extends StatelessWidget {
const TwoScreen({super.key});
@override
Widget build(BuildContext context) {
final arguments = ModalRoute.of(context)!.settings.arguments;
return MainLayout(
title: 'Two Screen',
children: [
Text(
'arguments 값: ${arguments}',
textAlign: TextAlign.center,
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('뒤로가기'),
)
],
);
}
}
3. Named Router를 사용한 데이터 전송
main.dart 파일에 routes: {} 선언
- '/'는 Home을 의미한다.
- 기존에 HomeScreen()을 사용하지 않고 routes:를 사용해 Home을 설정해준다.
- initialRoute는 초기 페이지를 설정한다.
main.dart 파일
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
import 'package:flutter_application_arrange/screen/home_screen.dart';
import 'package:flutter_application_arrange/screen/one_screen.dart';
import 'package:flutter_application_arrange/screen/three_screen.dart';
import 'package:flutter_application_arrange/screen/two_screen.dart';
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Appbar',
theme: ThemeData(
primarySwatch: Colors.purple,
),
//: HomeScreen(),
initialRoute: '/',
routes: {
'/': (context) => HomeScreen(),
'/one' : (context) => OneScreen(),
'/one/two' : (context) => TwoScreen(),
'/one/two/three' : (context) => ThreeScreen(),
},
),
);
}
Navigator.of(context).pushNamed()를 사용해 스크린 이동
pushNamed()는 String 값을 파라미터로 받으며, 위의 main.dart 파일에서 선언했던 라우트의 키(key) 값을 사용하면 값(value)에 해당하는 스크린으로 이동할 수 있다.
ElevatedButton(
onPressed: () {
Navigator.of(context).pushNamed('/one/two/three');
},
child: Text('세번째 페이지 이동'),
),
Navigator.of(context).pushNamed(arguemnts: )로 데이터 전송
ElevatedButton(
onPressed: () {
Navigator.of(context)
.pushNamed('/one/two/three', arguments: '세번째 페이지 데이터 전송');
},
child: Text('세번째 페이지 이동'),
),
ModalRoute.of(context)!.settings.arguments; 데이터 받기
three_screen.dart 파일
final arguments = ModalRoute.of(context)!.settings.arguments;
two_screen.dart 코드
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
import 'package:flutter_application_arrange/layout/main_layout.dart';
class TwoScreen extends StatelessWidget {
const TwoScreen({super.key});
@override
Widget build(BuildContext context) {
final arguments = ModalRoute.of(context)!.settings.arguments;
return MainLayout(
title: 'Two Screen',
children: [
Text(
'arguments 값: ${arguments}',
textAlign: TextAlign.center,
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('뒤로가기'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context)
.pushNamed('/one/two/three', arguments: '세번째 페이지 데이터 전송');
},
child: Text('세번째 페이지 이동'),
),
],
);
}
}
three_screen.dart 코드
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
import 'package:flutter_application_arrange/layout/main_layout.dart';
class ThreeScreen extends StatelessWidget {
const ThreeScreen({super.key});
@override
Widget build(BuildContext context) {
final arguments = ModalRoute.of(context)!.settings.arguments;
return MainLayout(
title: 'Three Screen',
children: [
Text(
'arguments값 : ${arguments}',
textAlign: TextAlign.center,
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('뒤로가기'),
)
],
);
}
}
유용한 Push 메서드
1. pushReplacement( ) 사용해 이전 라우트 지우기
스택에 쌓인 라우트
[HomeScreen(), OneScreen(), TwoScreen(), ThreeScreen()]
pushReplacement()를 사용하면 TwoScreen 가 지워진다.
[HomeScreen(), OneScreen(), ThreeScreen()]
ElevatedButton(
onPressed: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (BuildContext context) => ThreeScreen(),
),
);
},
child: Text('pushReplacement 사용'),
),
1-1. pushReplacementNamed( ) 사용해 이전 라우트 지우기
ElevatedButton(
onPressed: () {
Navigator.of(context).pushReplacementNamed(
'/one/two/three',
);
},
child: Text('pushReplacement 사용'),
),
Navigator.of(context).pushReplacement()를 사용하면 아래와 같이,
기존에는 세번째 페이지를 이동한 후에 '뒤로가기'를 클릭하면 'Two Screen'으로 이동을 하는데
pushReplacement()를 사용하면 Two Screen 부분이 사라지고 'First Screen'으로 이동하는 것을 확인할 수 있다.
2. pushAndRemoveUntil( ) 사용해 이전 라우트 지우기
해당 메서드는 삭제할 라우트의 범위를 지정할 수 있다.
ElevatedButton(
onPressed: () {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) => ThreeScreen()),
(route) => false);
},
child: Text('pushAndRemoveUntil 사용'),
)
(route)에 모든 라우트 값을 가져오고, 라우트마다 false를 반환하면 삭제가 되고, true를 반환하면 삭제가 되지 않는다.
그렇다면 아래처럼 (route) => false를 주고 실행하면 모든 라우트가 삭제가 된다.
(route) => false
아래와 같이 pushAndRemoveUntil 버튼을 클릭했을 때
뒤로가기를 누르면 검은화면이 뜬다.
이는 이전에 스택에 쌓은 모든 라우트가 삭제가 되었기 때문이다.
named Route로 Push를 한 경우 특정 라우트까지 삭제할 수 있다.
2-1. 특정 라우트까지 삭제
아래처럼 (route) => route.settings.name == '/'를 했을 때 Home에 해당하는 '/' 사이에 존재하는 라우트를 삭제한다.
그러면 '/'에 해당하는 부분까지 찾아가며, '/'에 해당하는 라우트가 나올 때까지 false를 리턴해 삭제를 한다.
[HomeScreen(), OneScreen(), TwoScreen(), ThreeScreen()] => [HomeScreen(), ThreeScreen()]
ElevatedButton(
onPressed: () {
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) => ThreeScreen()),
(route) => route.settings.name == '/');
},
child: Text('pushAndRemoveUntil 사용'),
)
뒤로가기를 눌렀을 때 HomeScreen으로 이동한다.
2-2. pushNamedAndRemoveUntil( ) 사용해 이전 라우트 지우기
Named를 사용하면 아래의 코드처럼 사용한다.
ElevatedButton(
onPressed: () {
Navigator.of(context).pushNamedAndRemoveUntil(
'/one/two/three', (route) => route.settings.name == '/');
},
child: Text('pushAndRemoveUntil 사용'),
)
유용한 Pop 메서드
1. Navigator.of(context).maybePop() 사용해 앱 종료 에러 막기
가장 첫번째 라우트에서 Navigator.of(context).pop()을 했을 때, 검은 화면이 나오며 앱이 종료가 되어버린다.
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('뒤로가기'),
)
혹시나 Navigator가 잘못 설정된 경우 검은 화면을 방지하기 위해 maybePop()을 사용한다.
maybePop()을 사용하면 더 이상 뒤로 갈 페이지가 없을 때 뒤로가기를 할 수 없도록 막아준다.
ElevatedButton(
onPressed: () {
Navigator.of(context).maybePop();
},
child: Text('maybePop 뒤로가기'),
)
2. Navigator.of(context).canPop() 사용해 pop이 가능한지 판단
HomeScreen에서 canPop 버튼을 눌렀을 때 false로 pop을 하면 안 된다는 것을 알려준다.
ElevatedButton(
onPressed: () {
print(Navigator.of(context).canPop());
},
child: Text('canPop 출력'),
),
3. WillPopScope 위젯 사용해 pop 막기
안드로이드 같은 경우에 Home Screen과 같이 첫 화면에서 뒤로가기 버튼을 눌렀을 떄 앱이 종료되는 경우가 있다. 그러나 특정 앱 같은 경우엔 뒤로가기를 했을 때 종료가 되는 상황을 막아줄 필요가 있다.
그때 사용하는 것은 WillPopScope 위젯이다.
해당 위젯의 리턴 값을 true로 하면 pop이 가능하고, false를 리턴하면 pop을 아예 불가능하게 설정할 수 있다.
일부러 pop 버튼을 만들면 막을 수는 없지만 해당 버튼이 없고 사용자가 시스템상에서 뒤로가기를 했을 때 종료를 막을 수는 있다.
- 1. WillPopScope() 위젯으로 감싸준다.
- 2. onWillPop:의 리턴 값을 true 또는 false로 지정한다.
- 3. onWillPop: 함수를 지정할 떄 async를 붙여준다.
home_screen.dart 파일 코드
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
import 'package:flutter_application_arrange/layout/main_layout.dart';
import 'one_screen.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
return false;
},
child: MainLayout(
title: 'Home Screen',
children: [
ElevatedButton(
onPressed: () async {
final result = await Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) =>
OneScreen(str: '메인에서 넘어온 데이터'),
),
);
print(result);
},
child: Text('첫번째 페이지 이동'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('뒤로가기'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).maybePop();
},
child: Text('maybePop 뒤로가기'),
),
ElevatedButton(
onPressed: () {
print(Navigator.of(context).canPop());
},
child: Text('canPop 출력'),
),
],
),
);
}
}
아래와 같이 canPop을 사용해 종료 조건을 줄 수가 있다.
return WillPopScope(
onWillPop: () async {
final canPop = Navigator.of(context).canPop();
return canPop;
},
'Flutter > Flutter Study' 카테고리의 다른 글
[Flutter] 세션(Session)과 토큰(Token)의 차이점 및 정의과 특징 (0) | 2023.05.20 |
---|---|
[Flutter] showDatePicker 한국어 바꾸기 / DatePicker 데이트피커 한국어 변경 (0) | 2023.04.28 |
[Flutter] BOTTM OVERFLOWED BY PIXELS 해결 방법 모음 (0) | 2023.04.22 |
[Flutter] 버튼(Button) - ElevatedButton, OutlinedButton, TextButton 꾸미기 (0) | 2023.03.14 |
[Flutter] 드로어(Drawer) 메뉴 만들기 | 플러터 네비게이션 드로어 (Navigation Drawer) 메뉴 (0) | 2023.02.15 |
[Flutter] AppBar 앱바 만들기 | DEBUG 리본 없애기 | 메뉴 아이콘 추가하기 (0) | 2023.02.15 |
댓글