Dandy Now!
  • [Flutter] "Do it! 플러터 앱 프로그래밍" - 내비게이션 활용하기 | 내비게이션 이해하기, 할 일을 기록하는 앱 만들기
    2022년 02월 15일 01시 07분 28초에 업로드 된 글입니다.
    작성자: DandyNow
    728x90
    반응형

    "조준수. (2021). Do it! 플러터 앱 프로그래밍. 이지스퍼블리싱", 8장을 실습하였다. 내비게이터와 라우트를 이용한 페이지 이동을 실습한 후 할 일 기록하는 앱을 만들어 보았다. 완성된 앱을 테스트하던 중 발생한 에러에 대해 예외 처리를 추가로 해주었다. 

     

    Do it! 플러터 앱 프로그래밍

    플러터 기본 & 고급 위젯은 물론오픈 API와 파이어베이스를 이용한 앱 개발부터 배포까지!플러터 SDK 2.x 버전을 반영한 개정판!이 책은 플러터의 기초부터 고급 활용법까지 다루어 다양한 영역에

    book.naver.com

     

    내비게이션 이해하기

    페이지 이동하기

    Navigitor는 스택을 이용해 페이지를 관리하는 클래스이다. 페이지의 이동은 push와 pop으로 이루어진다.

    // main.dart
    import 'package:flutter/material.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'SubPage Example',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: FirstPage(),
        );
      }
    }
    
    class FirstPage extends StatefulWidget {
      @override
      State<StatefulWidget> createState() => _FirstPage();
    }
    
    class _FirstPage extends State<FirstPage> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Sub Page Main'),
          ),
          body: Container(
            child: Center(
              child: Text('첫 번째 페이지'),
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              // Navigator는 스택을 이용해 페이지를 관리할 때 사용하는 클래스. of 함수는 현재 페이지를 나타내고, push 함수는 스택에 페이지를 쌓는 역할을 한다.
              Navigator.of(context)
                  // MaterialPageRoute 스타일로 페이지를 이동하게 한다.
                  .push(MaterialPageRoute(
                      builder: (context) => SecondPage())); // SecondPage를 쌓는다.
            },
            child: Icon(Icons.add),
          ),
        );
      }
    }
    
    class SecondPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Second Page'),
          ),
          body: Container(
            child: Center(
              child: ElevatedButton(
                onPressed: () {
                  // 이 페이지가 보인다는 것은 현재 스택 메모리 맨 위에 있다는 의미이다.
                  Navigator.of(context).pop(); // 현재 페이지 pop하여 종료
                },
                child: Text('돌아가기'),
              ),
            ),
          ),
        );
      }
    }

     

    [그림 1] 내비게이션 페이지 이동

     

    라우트로 페이지 이동하기

    라우트는 경로를 의미하는 용어이다. 이 기능을 이용하면 페이지 이동 기능을 더 편하게 구현할 수 있다.

    // main.dart
    import 'package:flutter/material.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'SubPage Example',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          // home: FirstPage(),
          // 라이트 설정
          initialRoute: '/', // 앱을 처음 시작했을 때 보여 줄 경로
          routes: {
            '/': (context) => FirstPage(),
            '/second': (context) => SecondPage()
          },
        );
      }
    }
    
    class FirstPage extends StatefulWidget {
      @override
      State<StatefulWidget> createState() => _FirstPage();
    }
    
    class _FirstPage extends State<FirstPage> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Sub Page Main'),
          ),
          body: Container(
            child: Center(
              child: Text('첫 번째 페이지'),
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              // Navigator는 스택을 이용해 페이지를 관리할 때 사용하는 클래스. of 함수는 현재 페이지를 나타내고, push 함수는 스택에 페이지를 쌓는 역할을 한다.
              Navigator.of(context).pushNamed('/second'); // 라우터 연결
              // MaterialPageRoute 스타일로 페이지를 이동하게 한다.
              // .push(MaterialPageRoute(
              //     builder: (context) => SecondPage())); // SecondPage를 쌓는다.
            },
            child: Icon(Icons.add),
          ),
        );
      }
    }
    
    class SecondPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Second Page'),
          ),
          body: Container(
            child: Center(
              child: ElevatedButton(
                onPressed: () {
                  // 이 페이지가 보인다는 것은 현재 스택 메모리 맨 위에 있다는 의미이다.
                  Navigator.of(context).pop(); // 현재 페이지 pop하여 종료
                },
                child: Text('돌아가기'),
              ),
            ),
          ),
        );
      }
    }

     


     

    할 일을 기록하는 앱 만들기

    페이지 이동하기

    세 개의 페이지를 이동하는 기능을 실습했다.

    // main.dart
    import 'package:flutter/material.dart';
    import 'subDetail.dart';
    import 'secondDetail.dart';
    import 'thirdPage.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      static const String _title = 'Widget Example';
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: _title,
          initialRoute: '/',
          // 경로 설정
          routes: {
            '/': (context) => SubDetail(),
            '/second': (context) => SecondDetail(),
            '/third': (context) => ThirdDetail(),
          },
        );
      }
    }

     

    // subDetail.dart
    import 'package:flutter/material.dart';
    
    class SubDetail extends StatefulWidget {
      @override
      State<StatefulWidget> createState() => _SubDetail();
    }
    
    class _SubDetail extends State<SubDetail> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Sub Detail Example'),
          ),
          body: Container(
            child: Center(
              child: ElevatedButton(
                onPressed: () {
                  // Navigator.of(context).pushReplacementNamed('/second'); // pushReplacementNamed 함수는 스택에 자료를 추가하지 않고 기존 자료를 교체한다.
                  Navigator.of(context)
                      .pushNamed('/second'); // pushNamed 함수는 새로운 자료를 추가한다.
                },
                child: Text('두 번째 페이지로 이동하기'),
              ),
            ),
          ),
        );
      }
    }

     

    // secondDetail.dart
    import 'package:flutter/material.dart';
    
    class SecondDetail extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Second Page'),
          ),
          body: Container(
            child: Center(
              child: ElevatedButton(
                onPressed: () {
                  Navigator.of(context)
                      .pushReplacementNamed('/third'); // pushReplacementNamed 함수를 호출해 이 페이지를 세 번째 페이지로 교체
                },
                child: Text('세 번째 페이지로 돌아가기'),
              ),
            ),
          ),
        );
      }
    }

     

    // thirdPage.dart
    import 'package:flutter/material.dart';
    
    class ThirdDetail extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Third Page'),
          ),
          body: Container(
            child: Center(
              child: ElevatedButton(
                onPressed: () {
                  Navigator.of(context).pop();
                },
                child: Text('첫 번째 페이지로 이동하기'),
              ),
            ),
          ),
        );
      }
    }

     

    [그림 2] 페이지 이동하기

     

    할 일 보기 기능 만들기

    페이지에 데이터를 전달하는 방법을 실습했다.

    // subDetail.dart
    import 'package:flutter/material.dart';
    
    class SubDetail extends StatefulWidget {
      @override
      State<StatefulWidget> createState() => _SubDetail();
    }
    
    class _SubDetail extends State<SubDetail> {
      List<String> todoList = new List.empty(growable: true);
    
      @override
      void initState() {
        super.initState();
        todoList.add('플러터 공부');
        todoList.add('자바스크립트 공부');
        todoList.add('플러터 토이 프로젝트');
        todoList.add('자바스크립트 토이 프로젝트');
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Sub Detail Example'),
          ),
          body: ListView.builder(
            itemBuilder: (context, index) {
              return Card(
                // InkWell 위젯은 탭, 더블탭, 롱탭 등 다양한 이벤트 처리를 할 수 있다.
                child: InkWell(
                  child: Text(
                    todoList[index],
                    style: TextStyle(fontSize: 30),
                  ),
                  onTap: () {
                    Navigator.of(context).pushNamed('/third',
                        arguments: todoList[
                            index]); // 첫 번째 인자로 지정한 경로로 두 번째 인자로 지정한 데이터를 전달한다
                  },
                ),
              );
            },
            itemCount: todoList.length,
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {},
            child: Icon(Icons.add),
          ),
        );
      }
    }

     

    // thirdPage.dart
    import 'package:flutter/material.dart';
    
    class ThirdDetail extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        // 라우터로 전달받은 데이터인 arguments 값이 어떤 자료형인지 알 수 없으므로 String으로 형변환한 다음 args에 넣는다.
        final String args = ModalRoute.of(context)!.settings.arguments.toString();
    
        return Scaffold(
          appBar: AppBar(
            title: Text('Third Page'),
          ),
          body: Container(
            child: Center(
              child: Text(
                args,
                style: TextStyle(fontSize: 30),
              ),
            ),
          ),
        );
      }
    }

     

    [그림 3] 할 일 보기 기능 만들기

     

    할 일 추가 기능 만들기

    할 일 추가 기능을 적용한 후 테스트하던 중 Second Page의 텍스트 필드에 입력 값이 없는 상태에서 뒤로 가기 버튼을 터치하니 [그림 5]와 같은 에러가 발생했다. 그래서 try catch문으로 예외처리를 해주었다.

    // subDetail.dart
    import 'package:flutter/material.dart';
    
    class SubDetail extends StatefulWidget {
      @override
      State<StatefulWidget> createState() => _SubDetail();
    }
    
    class _SubDetail extends State<SubDetail> {
      List<String> todoList = new List.empty(growable: true);
    
      @override
      void initState() {
        super.initState();
        todoList.add('플러터 공부');
        todoList.add('자바스크립트 공부');
        todoList.add('플러터 토이 프로젝트');
        todoList.add('자바스크립트 토이 프로젝트');
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Sub Detail Example'),
          ),
          body: ListView.builder(
            itemBuilder: (context, index) {
              return Card(
                // InkWell 위젯은 탭, 더블탭, 롱탭 등 다양한 이벤트 처리를 할 수 있다.
                child: InkWell(
                  child: Text(
                    todoList[index],
                    style: TextStyle(fontSize: 30),
                  ),
                  onTap: () {
                    Navigator.of(context).pushNamed('/third',
                        arguments: todoList[
                            index]); // 첫 번째 인자로 지정한 경로로 두 번째 인자로 지정한 데이터를 전달한다
                  },
                ),
              );
            },
            itemCount: todoList.length,
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              _addNavigation(context); // 할 일 추가 기능 함수 호출
            },
            child: Icon(Icons.add),
          ),
        );
      }
    
      // 할 일 추가 기능 만들기
      void _addNavigation(BuildContext context) async {
        final result = await Navigator.of(context).pushNamed('/second');
        setState(() {
          try {
            // result를 String 형변환하여 todoList에 add.
            todoList.add(result as String);
          } catch (e) {} // 할 일 추가하지 않고 뒤로 가기 처리. try catch 없으면 뒤로 가기 버튼 터치 시 [그림 5]와 같은 에러 발생
        });
      }
    }

     

    // secondDetail.dart
    import 'package:flutter/material.dart';
    
    class SecondDetail extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        TextEditingController controller =
            new TextEditingController(); // 할 일을 입력할 TextField용 변수
    
        return Scaffold(
          appBar: AppBar(
            title: Text('Second Page'),
          ),
          body: Container(
            child: Column(
              children: <Widget>[
                TextField(
                  controller: controller,
                  keyboardType: TextInputType.text,
                ),
                // TextField에 할 일을 입력하고 저장하기 버튼을 누르면 데이터를 전달한 후 현재 화면을 종료한다.
                ElevatedButton(
                  onPressed: () {
                    Navigator.of(context).pop(controller.value.text);
                  },
                  child: Text('저장하기'),
                ),
              ],
            ),
          ),
        );
      }
    }

     

    [그림 4] 할 일 추가 기능 적용

     

    [그림 5] 뒤로 가기 버튼 사용시 발생한 에러

    728x90
    반응형
    댓글