Flutter – Navigation (push)

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'),
        )