Flutter/Basic

[Flutter/Basic] 화면이동 Navigator - Push, Pop 정리

seunghwaan 2022. 1. 3. 17:17
반응형

Navigator Push

1. Navigator.push()로 화면 전환

전달할 데이터가 없는 경우에는 Navigator.push() 메소드를 통해 화면전환을 할 수 있습니다.

Navigator.push()에 전환할 페이지를 MaterialPageRoute()에 넣어주면 됩니다.

Navigator.push(context, MaterialPageRoute(builder: (context) => FirstScreen()));

main.dart

import 'package:flutter/material.dart';
import 'package:flutter_widgets/screen/button_above_keyboard.dart';
import 'package:flutter_widgets/screen/first_screen.dart';
import 'package:flutter_widgets/screen/second_screen.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Widgets',
      theme: ThemeData(
          primaryColor: Colors.blue,
          brightness: Brightness.dark),
      home: new HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: AppBar(
        title: Text("Flutter Widgets"),
      ),
      body: Container(
        child: Column(
          children: [
            Expanded(
              child: ListView(
                children: [
                  ListTile(
                    title: Text('FirstScreen'),
                    onTap: () {
                      // 화면전환(데이터 전달x)
                      Navigator.push(context, MaterialPageRoute(builder: (context) => FirstScreen())); 
                    },
                  ),
                ],
              ),
            )
          ],
        ),
      ),
    );
  }
}

first_screen.dart

import 'package:flutter/material.dart';
import 'package:flutter_widgets/text_style.dart';

class FirstScreen extends StatefulWidget {
  @override
  _FirstScreenState createState() => _FirstScreenState();
}

class _FirstScreenState extends State<FirstScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(
          'FirstScreen',
          style: kNotoSansMedium16.copyWith(color: Colors.white),
        ),
      ),
    );
  }
}

 

2. Navigator.pushNamed()를 통한 화면전환

MaterialApp의 속성인 initialRoute와 routes를 통해 경로를 제공할 수 있습니다.

 

initialRoute는 앱의 시작화면을 지정할 수 있고,

routes에 ScreenName과 Widget을 Map으로 만들어 지정하면 String을 통해 Navigator.pushNamed()를 통해 간단하게 화면전환을 할 수 있습니다.

MaterialApp(
  title: 'Flutter Widgets',
  theme: ThemeData(
    primaryColor: Colors.blue,
    brightness: Brightness.dark),
  initialRoute: HomeScreen.routeName,
  routes: routes
)

final routes = {
  HomeScreen.routeName: (context) => HomeScreen(),
  SecondScreen.routeName: (context) => SecondScreen(),
};

Navigator.pushNamed(context, SecondScreen.routeName);

혹은 routes를 MaterialApp에서 지정하지 않고 화면전환을 하려면 아래와 같이 사용하면 됩니다.

Navigator.push(
    context,
    MaterialPageRoute(
      settings: const RouteSettings(name: SecondScreen.routeName),
      builder: (context) => SecondScreen(),
    ));

main.dart

import 'package:flutter/material.dart';
import 'package:flutter_widgets/screen/button_above_keyboard.dart';
import 'package:flutter_widgets/screen/first_screen.dart';
import 'package:flutter_widgets/screen/second_screen.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Widgets',
      theme: ThemeData(
          primaryColor: Colors.blue,
          brightness: Brightness.dark),
      routes: routes,
      home: new HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: AppBar(
        title: Text("Flutter Widgets"),
      ),
      body: Container(
        child: Column(
          children: [
            Expanded(
              child: ListView(
                children: [
                  ListTile(
                    title: Text('SecondScreen'),
                    onTap: () {
                      // Navigator.pushNamed()를 통한 화면전환
                      Navigator.pushNamed(
                        context,
                        SecondScreen.routeName,
                      );
                    },
                  ),
                ],
              ),
            )
          ],
        ),
      ),
    );
  }
}

final routes = {
  HomeScreen.routeName: (context) => HomeScreen(),
  SecondScreen.routeName: (context) => SecondScreen(),
};

second_screen.dart

import 'package:flutter/material.dart';
import 'package:flutter_widgets/text_style.dart';

class SecondScreen extends StatefulWidget {

  static String routeName = "/second_screen";

  @override
  _SecondScreenState createState() => _SecondScreenState();
}

class _SecondScreenState extends State<SecondScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(
          'SecondScreen',
          style: kNotoSansMedium16.copyWith(color: Colors.white),
        ),
      ),
    );
  }
}

 

3. Navigator.push(), Navigator.pushNamed()를 통한 화면전환 with Arguments(데이터 전달)

3 - (1) Navigator.push()를 통한 데이터 전달

Navigator.push()에 MaterialPageRoute인자를 이용하여 데이터를 전달할 수 있습니다.

Navigator.push(context, MaterialPageRoute(builder: (context) => ThirdScreen(update: true)));

main.dart

import 'package:flutter/material.dart';

import 'package:flutter_widgets/screen/button_above_keyboard.dart';
import 'package:flutter_widgets/screen/first_screen.dart';
import 'package:flutter_widgets/screen/second_screen.dart';
import 'package:flutter_widgets/screen/third_screen.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Widgets',
      theme: ThemeData(primaryColor: Colors.blue, brightness: Brightness.dark),
      initialRoute: HomeScreen.routeName,
      routes: routes,
    );
  }
}

class HomeScreen extends StatelessWidget {
  static String routeName = "/";

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: AppBar(
        title: Text("Flutter Widgets"),
      ),
      body: Container(
        child: Column(
          children: [
            Expanded(
              child: ListView(
                children: [
                  ListTile(
                    title: Text('ThirdScreen'),
                    onTap: () {
                      Navigator.push(context, MaterialPageRoute(builder: (context) => ThirdScreen(update: true)));
                    },
                  ),
                ],
              ),
            )
          ],
        ),
      ),
    );
  }
}

third_screen.dart

import 'package:flutter/material.dart';
import 'package:flutter_widgets/text_style.dart';

class ThirdScreen extends StatefulWidget {
  const ThirdScreen({required this.update, Key? key}) : super(key: key);

  final bool update;

  static const String routeName = "/third_screen";

  @override
  _ThirdScreenState createState() => _ThirdScreenState();
}

class _ThirdScreenState extends State<ThirdScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(
          'ThirdScreen',
          style: kNotoSansMedium16.copyWith(color: Colors.white),
        ),
      ),
      body: SafeArea(
        child: Column(
          children: [
            Spacer(),
            Center(
              child: Text(
                '${widget.update}',
                style: kNotoSansMedium16.copyWith(color: Colors.white),
              ),
            ),
            Spacer(),
          ],
        ),
      ),
    );
  }
}

3 - (2) Navigator.pushNamed()를 통한 데이터 전달

routeName과 데이터를 포함하는 MaterialPageRoute를 사용하려면 Map을 통해 MaterialApp의 onGenerateRoute를 통해 제공해야 합니다.

여기에 Map을 통해 데이터를 넣어주면, Navigator.pushNamed()를 통해 화면전환을 할 수 있습니다.

 MaterialApp(
       title: 'Flutter Widgets',
       theme: ThemeData(
           primaryColor: Colors.blue,
           brightness: Brightness.dark),
       initialRoute: HomeScreen.routeName,
       onGenerateRoute: generateRoute,
       routes: routes,
     );
   }

  Route? generateRoute(RouteSettings routeSettings) {
    switch (routeSettings.name) {
      case ThirdScreen.routeName:
        return MaterialPageRoute(
          builder: (context) {
            var map = routeSettings.arguments as Map<String, dynamic>;
            return ThirdScreen(
              update: map['update'] as bool,
            );
          },
          settings: routeSettings,
        );
      default:
        return null;
    }
  }

 

import 'package:flutter/material.dart';
import 'package:flutter_widgets/screen/button_above_keyboard.dart';
import 'package:flutter_widgets/screen/first_screen.dart';
import 'package:flutter_widgets/screen/second_screen.dart';
import 'package:flutter_widgets/screen/third_screen.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Widgets',
      theme: ThemeData(
          primaryColor: Colors.blue,
          brightness: Brightness.dark),
      initialRoute: HomeScreen.routeName,
      onGenerateRoute: generateRoute,
      routes: routes,
    );
  }

  Route? generateRoute(RouteSettings routeSettings) {
    switch (routeSettings.name) {
      case ThirdScreen.routeName:
        return MaterialPageRoute(
          builder: (context) {
            var map = routeSettings.arguments as Map<String, dynamic>;
            return ThirdScreen(
              update: map['update'] as bool,
            );
          },
          settings: routeSettings,
        );
      default:
        return null;
    }
  }
}

class HomeScreen extends StatelessWidget {

  static String routeName = "/";

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: AppBar(
        title: Text("Flutter Widgets"),
      ),
      body: Container(
        child: Column(
          children: [
            Expanded(
              child: ListView(
                children: [
                  ListTile(
                    title: Text('ThirdScreen'),
                    onTap: () {
                      // 화면전환
                      Navigator.pushNamed(context, ThirdScreen.routeName, arguments: {"update": true});
                    },
                  ),
                ],
              ),
            )
          ],
        ),
      ),
    );
  }
}

4. Navigator.pushNamedAndRemoveUntil()을 이용한 화면전환

 

Navigator.pushNamedAndRemoveUntil()를 이용하면 페이지를 push()함과 동시에 Stack에 쌓여있던 페이지들을 제거할 수 있습니다.

Navigator.pushNamedAndRemoveUntil(context, newRouteName, (route) => false);

 

예시)

 

이전 스택 페이지들 전부 제거

Navigator.pushNamedAndRemoveUntil(context, ThirdScreen.routeName, (route) => false, arguments: {"update": true});

 

스택의 특정 페이지 이전까지 모두 제거

Navigator.pushNamedAndRemoveUntil(context, ThirdScreen.routeName, ModalRoute.withName(SecondScreen.routeName), arguments: {"update": true});

 

second_screen.dart

import 'package:flutter/material.dart';
import 'package:flutter_widgets/main.dart';
import 'package:flutter_widgets/screen/third_screen.dart';
import 'package:flutter_widgets/text_style.dart';

class SecondScreen extends StatefulWidget {
  static String routeName = "/second_screen";

  @override
  _SecondScreenState createState() => _SecondScreenState();
}

class _SecondScreenState extends State<SecondScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(
          'SecondScreen',
          style: kNotoSansMedium16.copyWith(color: Colors.white),
        ),
      ),
      body: SafeArea(
        child: Column(
          children: [
            InkWell(
              onTap: () {
                Navigator.pushNamedAndRemoveUntil(context, ThirdScreen.routeName, ModalRoute.withName(SecondScreen.routeName), arguments: {"update": true});
              },
              child: Text(
                "GoTo ThirdScreen",
                style: kNotoSansMedium16.copyWith(color: Colors.white),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

5. Navigator.pushReplacementNamed()를 통한 화면전환

Navigator.pushReplacementNamed()를 이용하면 현재 페이지를 스택에서 제거하고 이동할 페이지를 스택에 집어넣을 수 있습니다.

Navigator.pushReplacementNamed(context, ThirdScreen.routeName);

간단하므로 예제는 생략하겠습니다.

 

Navigator Pop

1. Navigator.pop()

Navigator.pop()을 이용하면 현재 페이지를 스택에서 제거할 수 있습니다.

 

Navigator.pop(context);

예시)

third_screen.dart

import 'package:flutter/material.dart';
import 'package:flutter_widgets/text_style.dart';

class ThirdScreen extends StatefulWidget {
  const ThirdScreen({required this.update, Key? key}) : super(key: key);

  final bool update;

  static const String routeName = "/third_screen";

  @override
  _ThirdScreenState createState() => _ThirdScreenState();
}

class _ThirdScreenState extends State<ThirdScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: IconButton(
          onPressed: () {
            Navigator.pop(context);
          },
          icon: Icon(Icons.arrow_back),
        ),
        title: Text(
          'ThirdScreen',
          style: kNotoSansMedium16.copyWith(color: Colors.white),
        ),
      ),
    );
  }
}

2. Navigator.popUntil()

스택에 존재하는 페이지와 일치하는 페이지까지 스택에서 제거합니다.

Navigator.popUntil(context, ModalRoute.withName(HomeScreen.routeName));

예시)

third_screen.dart

import 'package:flutter/material.dart';
import 'package:flutter_widgets/main.dart';
import 'package:flutter_widgets/text_style.dart';

class ThirdScreen extends StatefulWidget {
  const ThirdScreen({required this.update, Key? key}) : super(key: key);

  final bool update;

  static const String routeName = "/third_screen";

  @override
  _ThirdScreenState createState() => _ThirdScreenState();
}

class _ThirdScreenState extends State<ThirdScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: IconButton(
          onPressed: () {
            // Navigator.pop(context);
            Navigator.popUntil(context, ModalRoute.withName(HomeScreen.routeName));
          },
          icon: Icon(Icons.arrow_back),
        ),
        title: Text(
          'ThirdScreen',
          style: kNotoSansMedium16.copyWith(color: Colors.white),
        ),
      ),
      body: SafeArea(
        child: Column(
          children: [
            Spacer(),
            Center(
              child: Text(
                '${widget.update}',
                style: kNotoSansMedium16.copyWith(color: Colors.white),
              ),
            ),
            Spacer(),
          ],
        ),
      ),
    );
  }
}

3. Navigator.popAndPushNamed()

현재 페이지를 스택에서 제거함과 동시에 새로운 페이지를 스택에 추가합니다.

Navigator.popAndPushNamed(context, SecondScreen.routeName);

예시)

third_screen.dart

import 'package:flutter/material.dart';
import 'package:flutter_widgets/main.dart';
import 'package:flutter_widgets/screen/first_screen.dart';
import 'package:flutter_widgets/screen/second_screen.dart';
import 'package:flutter_widgets/text_style.dart';

class ThirdScreen extends StatefulWidget {
  const ThirdScreen({required this.update, Key? key}) : super(key: key);

  final bool update;

  static const String routeName = "/third_screen";

  @override
  _ThirdScreenState createState() => _ThirdScreenState();
}

class _ThirdScreenState extends State<ThirdScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: IconButton(
          onPressed: () {
            // Navigator.pop(context);
            // Navigator.popUntil(context, ModalRoute.withName(HomeScreen.routeName));
            Navigator.popAndPushNamed(context, SecondScreen.routeName);
          },
          icon: Icon(Icons.arrow_back),
        ),
        title: Text(
          'ThirdScreen',
          style: kNotoSansMedium16.copyWith(color: Colors.white),
        ),
      ),
      body: SafeArea(
        child: Column(
          children: [
            Spacer(),
            Center(
              child: Text(
                '${widget.update}',
                style: kNotoSansMedium16.copyWith(color: Colors.white),
              ),
            ),
            Spacer(),
          ],
        ),
      ),
    );
  }
}

 

반응형