본문 바로가기
Flutter + Dart/Flutter + Dart 공부

화면 전환 (Navigator) 기초 학습

by GREEN나무 2025. 5. 14.
728x90

페이지 이동

  한 파일 내에서

  다른 파일의 화면으로 이동하기

 

학습 목표

  • Flutter의 기본 내비게이션(Navigator) 개념 이해
  • 화면 간 이동(push, pop) 구현
  • MaterialPageRoute와 Named Route 설정 방법 학습
  • 간단한 데이터 전달(Arguments) 준비

1. 프로젝트 준비

  1. Flutter 프로젝트 생성
  2. pubspec.yaml에 특별한 패키지 추가는 필요 없고, 기본 material 위젯 사용

2. 화면 구성

기본 화면(메인)                                            일기 작성 화면                                           상세 화면(DetailPage)

 


🛠 예제 코드

1. MaterialApp 설정

import 'package:flutter/material.dart';

void main() => runApp(const DiaryApp());

class DiaryApp extends StatelessWidget {
  const DiaryApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Diary App',
      // Named Routes 등록
      routes: {
        '/': (context) => const HomePage(),
        '/write': (context) => const WritePage(),
        '/detail': (context) => const DetailPage(),
      },
      initialRoute: '/',
    );
  }
}

2. HomePage – 메인 화면

// 기본 화면(메인) 구성
class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('메인 화면')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: () {
                Navigator.pushNamed(context, '/write');
              },
              child: const Text('✍️ 일기 작성 화면으로 이동'),
            ),
            ElevatedButton(
              onPressed: () {
                Navigator.pushNamed(
                  context,
                  '/detail',
                  arguments: '2025년 5월 14일의 일기',
                );
              },
              child: const Text('📖 상세 화면으로 이동'),
            ),
          ],
        ),
      ),
    );
  }
}

3. WritePage – 작성 화면

// 일기 작성 화면 - 일기 작성
class WritePage extends StatelessWidget {
  const WritePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('일기 작성')),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pop(context); // 작성 완료 후 돌아가기
          },
          child: const Text('✅ 작성 완료'),
        ),
      ),
    );
  }
}

4. DetailPage – 전달 받은 데이터를 보여주는 화면

// 상세 화면 - 일기 상세 보기
class DetailPage extends StatelessWidget {
  const DetailPage({super.key});

  @override
  Widget build(BuildContext context) {
    // arguments 받기
    final itemTitle = ModalRoute.of(context)!.settings.arguments as String?;

    return Scaffold(
      appBar: AppBar(title: const Text('상세 화면')),
      body: Center(
        child: Text(
          itemTitle != null ? '$itemTitle 상세 보기' : '상세 정보를 불러올 수 없음',
          style: const TextStyle(fontSize: 18),
        ),
      ),
    );
  }
}

 

home_page.dart 전체 코드

더보기
import 'package:flutter/material.dart';

void main() => runApp(const DiaryApp());

class DiaryApp extends StatelessWidget {
  const DiaryApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Diary App',
      // Named Routes 등록
      routes: {
        '/': (context) => const HomePage(),
        '/write': (context) => const WritePage(),
        '/detail': (context) => const DetailPage(),
      },
      initialRoute: '/',
    );
  }
}

// 기본 화면(메인) 구성
class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('메인 화면')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: () {
                Navigator.pushNamed(context, '/write');
              },
              child: const Text('✍️ 일기 작성 화면으로 이동'),
            ),
            ElevatedButton(
              onPressed: () {
                Navigator.pushNamed(
                  context,
                  '/detail',
                  arguments: '2025년 5월 14일의 일기',
                );
              },
              child: const Text('📖 상세 화면으로 이동'),
            ),
          ],
        ),
      ),
    );
  }
}

// 일기 작성 화면 - 일기 작성
class WritePage extends StatelessWidget {
  const WritePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('일기 작성')),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pop(context); // 작성 완료 후 돌아가기
          },
          child: const Text('✅ 작성 완료'),
        ),
      ),
    );
  }
}

// 상세 화면 - 일기 상세 보기
class DetailPage extends StatelessWidget {
  const DetailPage({super.key});

  @override
  Widget build(BuildContext context) {
    // arguments 받기
    final itemTitle = ModalRoute.of(context)!.settings.arguments as String?;

    return Scaffold(
      appBar: AppBar(title: const Text('상세 화면')),
      body: Center(
        child: Text(
          itemTitle != null ? '$itemTitle 상세 보기' : '상세 정보를 불러올 수 없음',
          style: const TextStyle(fontSize: 18),
        ),
      ),
    );
  }
}

3.🧭 Named Route와 Arguments

📌 Named Route란?

Flutter 앱은 여러 화면(Route)으로 구성돼요.
각 화면을 MaterialApp의 routes 속성에 등록해두면, 경로 이름만으로 간편하게 화면 전환을 할 수 있습니다.

✅ 기본 구조

MaterialApp(
  initialRoute: '/',
  routes: {
    '/': (context) => const HomePage(),
    '/write': (context) => const WritePage(),
    '/detail': (context) => const DetailPage(),
  },
);

✅ 화면 이동

Navigator.pushNamed(context, '/write');

📦 화면 간 데이터 전달 (Arguments)

📨 전달

화면 이동 시 데이터를 함께 보내고 싶다면 arguments를 활용하세요:

Navigator.pushNamed(
  context,
  '/detail',
  arguments: '전달할 텍스트 또는 데이터',
);

📬 수신

받는 쪽에서는 다음과 같이 데이터를 꺼낼 수 있어요:

final data = ModalRoute.of(context)!.settings.arguments as String;
@override
Widget build(BuildContext context) {
  final data = ModalRoute.of(context)!.settings.arguments as String;
  return Scaffold(
    appBar: AppBar(title: const Text('상세 화면')),
    body: Center(child: Text('전달 받은 값: $data')),
  );
}

🧭 요약 표

항목 사용 방법
📘 Named Route 등록 MaterialApp(routes: {...})
📤 데이터 전달 Navigator.pushNamed(context, '/route', arguments: data)
📥 데이터 수신 ModalRoute.of(context)!.settings.arguments

✏️ 실습 과제

🛠 메인 화면 (HomePage)

  • "일기 작성" 버튼 → /write로 이동
Navigator.pushNamed(context, '/write');

🛠 일기 작성 화면 (WritePage)

  • "작성 완료" 버튼 → pop()으로 홈으로 복귀
Navigator.pop(context); // 데이터 전달은 아직 없음

🛠 상세 화면 (DetailPage)

  • 목록 클릭 시 /detail로 이동하며 arguments 전달
Navigator.pushNamed(
  context,
  '/detail',
  arguments: '예시 제목',
);
  • 전달 받은 데이터를 활용해 화면 구성
  • 추가로 “삭제”, “수정” 버튼을 아래처럼 배치
body: Padding(
  padding: const EdgeInsets.all(16),
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.stretch,
    children: [
      Text('$itemTitle 상세 보기'),
      ElevatedButton(
        onPressed: () {
          Navigator.pop(context, {'action': 'delete', 'item': itemTitle});
        },
        child: const Text('삭제'),
      ),
      ElevatedButton(
        onPressed: () {
          Navigator.pop(context, {'action': 'edit', 'item': itemTitle});
        },
        child: const Text('수정'),
      ),
    ],
  ),
),

 


4. 🔁 관련된 기능 소개

▶️ pushReplacement

기존 화면을 대체하고 새 화면으로 이동할 때 사용

Navigator.pushReplacementNamed(context, '/write');

▶️ arguments에 Map 전달

데이터를 좀 더 유연하게 전달할 수 있어요

Navigator.pushNamed(
  context,
  '/detail',
  arguments: {
    'title': 'Flutter 일기',
    'id': 42,
  },
);

5. 🌱 더 추가하고  싶은 기능 : 작성 데이터 전달 & 리스트 갱신

  1. WritePage에 TextField 추가
  2. TextEditingController 사용
  3. 작성 완료 시 pop(context, controller.text)
  4. HomePage에서 then()으로 결과 받기
  5. 리스트에 추가하고 setState 호출
Navigator.push<String>(
  context,
  MaterialPageRoute(builder: (_) => const WritePage()),
).then((result) {
  if (result != null && result.isNotEmpty) {
    setState(() {
      entries.add(result);
    });
  }
});
기능 설명
🧠 Map으로 복합 데이터 전달 {title: ..., id: ...} 형태
🔁 pushReplacementNamed 기존 화면 스택을 덮는 방식
🔄 pop(context, result) 결과 반환으로 리스트 갱신
🗃️ sqflite 연동 데이터 영구 저장
📝 수정 기능 텍스트 초기값 지정해 TextField에 주입