push는 기본적으로 stack 구조이다. stack에 차례대로 각 페이지들이 쌓이고, pop시에 끝에서부터 꺼낸다.
스크린 데이터 주고 받기 – (1)
import 'package:flutter/material.dart'; import 'package:test_1/layout/main_layout.dart'; import 'package:test_1/screen/route_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 { // 데이터를 받을때는 async await final result = await Navigator.of(context).push( // result로 pop데이터 받아오기 MaterialPageRoute( builder: (BuildContext context) => RouteOneScreen( number : 123, // 넘길 데이터 ), ), ); print(result); }, child: Text('Push'), ) ], ); } }
import 'package:flutter/material.dart'; import 'package:test_1/layout/main_layout.dart'; class RouteOneScreen extends StatelessWidget { final int number; // 받아와서 저장할 변수 선언 const RouteOneScreen({ required this.number, super.key, }); @override Widget build(BuildContext context) { return MainLayout( title: 'Route one', children: [ Text( number.toString(), textAlign: TextAlign.center, ), ElevatedButton( onPressed: () { Navigator.of(context).pop(456); // 다시 넘길 데이터 }, child: Text('Pop'), ) ], ); } }
스크린 데이터 주고받기 / arguments 통해 전달- (2)
ElevatedButton( onPressed: () { Navigator.of(context).push( MaterialPageRoute( builder: (_) => RouteTwoScreen(), settings: RouteSettings( arguments: 789, // arguments 로 전달 ), ), ); }, child: Text('Push'), )
import 'package:flutter/material.dart'; import 'package:test_1/layout/main_layout.dart'; class RouteTwoScreen extends StatelessWidget { const RouteTwoScreen({super.key}); @override Widget build(BuildContext context) { final arguments = ModalRoute.of(context)!.settings.arguments; // 데이터 받아오기/ return MainLayout( title: 'Route two', children: [ Text( 'arguments : ${arguments}', textAlign: TextAlign.center, ), ElevatedButton( onPressed: () { Navigator.of(context).pop(); }, child: Text('Pop'), ) ], ); } }
웹처럼 Routing / pushNamed로 푸시
import 'package:flutter/material.dart'; import 'package:test_1/screen/home_screen.dart'; import 'package:test_1/screen/route_one_screen.dart'; import 'package:test_1/screen/route_three_screen.dart'; import 'package:test_1/screen/route_two_screen.dart'; void main() { runApp(MaterialApp( initialRoute: '/', // 시작 페이지 /* * 이 방식을 named route라 부름 * named route를 쓰면, argument를 쓰는게 스크린간 데이터를 전달하는 가장 쉬운 방법임 * */ routes: { '/' : (context)=> HomeScreen(), //home '/one' : (context) => RouteOneScreen(), '/two': (context) => RouteTwoScreen(), '/three': (context) => RouteThreeScreen(), }, )); }
ElevatedButton( onPressed: () { Navigator.of(context).pushNamed('/three'); // routes에 정의된 경로로 푸시 }, child: Text('Push Named'), )
routes에 정의한 경로로 스크린 전환을 할 수 있다. 이 때 데이터를 같이 전달하고 싶으면 ? 바로 arguments와 함께 넘겨주면 된다.
ElevatedButton( onPressed: () { Navigator.of(context).pushNamed('/three', arguments: 999); }, child: Text('Push Named'), )
import 'package:flutter/material.dart'; import 'package:test_1/layout/main_layout.dart'; class RouteThreeScreen extends StatelessWidget { const RouteThreeScreen({super.key}); @override Widget build(BuildContext context) { final argument = ModalRoute.of(context)!.settings.arguments; // arguments로 전달된 값 받아오기 return MainLayout( title: 'Route three', children: [ Text( 'argument :${argument}', textAlign: TextAlign.center, ), ElevatedButton( onPressed: () { Navigator.of(context).pop(); }, child: Text('Pop'), ) ], ); } }
각 Route 페이지를 관리하는 좋은 방법
import 'package:flutter/material.dart'; import 'package:test_1/screen/home_screen.dart'; import 'package:test_1/screen/route_one_screen.dart'; import 'package:test_1/screen/route_three_screen.dart'; import 'package:test_1/screen/route_two_screen.dart'; const HOME_ROUTE = '/'; const ONE_ROUTE = '/one'; const TWO_ROUTE = '/one'; const THREE_ROUTE = '/one'; void main() { runApp(MaterialApp( initialRoute: '/', // 시작 페이지 /* * 이 방식을 named route라 부름 * named route를 쓰면, argument를 쓰는게 스크린간 데이터를 전달하는 가장 쉬운 방법임 * */ routes: { HOME_ROUTE : (context)=> HomeScreen(), //home ONE_ROUTE : (context) => RouteOneScreen(), TWO_ROUTE : (context) => RouteTwoScreen(), THREE_ROUTE : (context) => RouteThreeScreen(), }, )); }
또 다른 스크린간 push 방법 (pushReplacement)
ElevatedButton( onPressed: () { /* * 기존 push 순서 : [HomeScreen(), RouteOne(), RouteTwo(), RouteThree()] * pushReplace : [HomeScreen(), RouteOne(), RouteThree()] * */ Navigator.of(context).pushReplacement( // pushReplace 를 사용하면 RouteThree()가 Two를 대체함 (pop시 바로 one으로 감) MaterialPageRoute( builder: (_) => RouteThreeScreen(), ), ); }, child: Text('push replacement'), )
또 다른 스크린간 push 방법 (pushReplacementNamed)
ElevatedButton( onPressed: () { Navigator.of(context).pushReplacementNamed( '/three', ); }, child: Text('push replacement'), )
push 이후 이전 모든 route를 스택에서 삭제
ElevatedButton( onPressed: () { Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( builder: (_) => RouteThreeScreen(), // three screen 으로 이동 후, 뒤로 가기 버튼 없음 (이전 모든 route를 스택에서 지움) ), (route) => false); // navigation stack에 있는 모든 route를 받아서 각각 false를 리턴함 // (route) => true); // true를 리턴하면 stack 에 있는 모든 route 들을 지우지 않음 }, child: Text('PushAndRemoveUntil'), )
ElevatedButton( onPressed: () { Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( builder: (_) => RouteThreeScreen(), // three screen 으로 이동 후, 뒤로 가기 버튼 없음 (이전 모든 route를 스택에서 지움) ), // (route) => false); // navigation stack에 있는 모든 route를 받아서 각각 false를 리턴함 // (route) => true); // true를 리턴하면 stack 에 있는 모든 route 들을 지우지 않음 (route) => route.settings.name == '/', // '/' route만 true를 리턴함. pop 시 바로 home screen으로 감(나머지는 다 지워짐) ); }, child: Text('PushAndRemoveUntil'), )
NamedRoute 도 route 스택에서 삭제 처리 가능
ElevatedButton( onPressed: () { Navigator.of(context).pushNamedAndRemoveUntil( '/three', // named route // (route) => false); // navigation stack에 있는 모든 route를 받아서 각각 false를 리턴함 // (route) => true); // true를 리턴하면 stack 에 있는 모든 route 들을 지우지 않음 (route) => route.settings.name == '/', // '/' route만 true를 리턴함. pop 시 바로 home screen으로 감(나머지는 다 지워짐) ); }, child: Text('PushAndRemoveUntil'), )