이전 기사 뒤에 코드를 작성하고,
버튼을 클릭할 때 데이터 전송까지 수행되도록 합니다.
이전 코드는 아래 문서를 참조하십시오.
중복 코드 MainLayout라는 위젯으로 다른 파일로 당겨 관리했다.
(플러터) 버튼(버튼) – ElevatedButton, OutlinedButton, TextButton
1. 변수와 생성자, 팝을 통한 데이터 전송
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:를 사용하여 홈을 설정합니다.
- 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 파일에서 선언한 루트의 키 값을 사용하여 값(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('뒤로가기'),
)
),
);
}
}
편리한 푸시 방법
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() 를 사용하면 다음과 같이,
기존에는 3페이지를 이동한 후 뒤로를 클릭하면 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 사용'),
)
(루트)에 모든 루트 값을 가져오고 각 루트에 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('뒤로가기'),
)
네비게이터가 올바르게 설정되지 않은 경우 검은 화면을 방지하기 위해 maybePop()사용합니다.
maybePop() 을 사용하면 더 이상 뒤로 페이지가 없을 때 되돌릴 수 없습니다.
ElevatedButton(
onPressed: () {
Navigator.of(context).maybePop();
},
child: Text('maybePop 뒤로가기'),
)
2. Navigator.of(context).canPop() 사용하여 팝이 가능한지 결정하기
HomeScreen에서 canPop 버튼을 누를 때 false로 팝하지 마십시오.
ElevatedButton(
onPressed: () {
print(Navigator.of(context).canPop());
},
child: Text('canPop 출력'),
),
3. WillPopScope 위젯을 사용하여 pop을 방지
Android와 같은 경우 Home Screen과 같이 첫 화면에서 돌아가기 버튼을 누르면 앱이 종료될 수 있다.
그러나 특정 앱과 같은 경우에는 돌아가기를 할 때 종료되는 상황을 막을 필요가 있다.
그때 사용하는 것은 WillPopScope 위젯입니다.
해당 위젯의 반환 값을 true로 설정하면 pop이 가능하고 false를 반환하면 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;
},