ข้ามไปยังเนื้อหา

พื้นฐาน Navigator

ใน React คุณเรียก useNavigate() เพื่อรับฟังก์ชัน navigate แล้วเรียก navigate('/detail') เพื่อเปลี่ยน route ปัจจุบัน สิ่งที่เทียบเท่ากันใน Flutter คือ Navigator.push(context, route) — แต่แทนที่จะเป็น URL string คุณส่ง Route object ที่รู้วิธีสร้างหน้าจอถัดไป ความแตกต่างหลักคือ Flutter รักษา widget stack จริง ๆ: หน้าจอก่อนหน้ายังคงมีชีวิตอยู่ในหน่วยความจำใต้อันใหม่ เหมือนกับที่ browser history เก็บหน้าก่อนหน้าไว้ใน session ของแท็บ

ปุ่มย้อนกลับ Android และ gesture swipe-back ของ iOS ถูกจัดการโดยอัตโนมัติโดย Navigator — คุณไม่จำเป็นต้องเชื่อมต่อ back handler เว้นแต่ต้องการ intercept เอง

React
// React — navigate programmatically
import { useNavigate } from 'react-router-dom';
function HomeScreen() {
const navigate = useNavigate();
return (
<button onClick={() => navigate('/detail')}>
Go to Detail
</button>
);
}
// Go back
function DetailScreen() {
const navigate = useNavigate();
return (
<button onClick={() => navigate(-1)}>
Back
</button>
);
}
Flutter
// Flutter — push / pop
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DetailScreen(),
),
);
},
child: const Text('Go to Detail'),
);
}
}
// Go back
class DetailScreen extends StatelessWidget {
const DetailScreen({super.key});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () => Navigator.pop(context),
child: const Text('Back'),
);
}
}

ตั้งแต่ Flutter 3.38 page transition เริ่มต้นบน Android คือ PredictiveBackPageTransitionsBuilder เมื่อผู้ใช้ลากนิ้วจากขอบจอ Android จะค่อย ๆ ลอก route ปัจจุบันออกและแสดง preview ของหน้าจอที่อยู่ข้างล่างแบบ real-time ผู้ใช้จึงเห็นก่อนว่า “ย้อนกลับ” จะไปที่ไหนก่อนตัดสินใจปล่อยนิ้ว บนเครื่องที่ไม่รองรับ predictive back จะ fallback ไปเป็น FadeForwardsPageTransitionsBuilder

สิ่งที่ใกล้เคียงที่สุดที่คุณคุ้นเคยคือ edge-swipe preview ของ Safari บน iOS หรือ screen transition ใน React Native ที่ต่อกับ gesture handler ส่วนบน browser history.back() ไม่มี preview ให้เลย หน้าเว็บถูกแทนที่ทันที

คุณได้สิ่งนี้มาฟรีจาก MaterialPageRoute ไม่ต้อง opt-in อะไร ถ้าอยากกลับไปใช้ animation แบบก่อน 3.38 ให้ set ใน theme ตรง ๆ:

MaterialApp(
theme: ThemeData(
pageTransitionsTheme: const PageTransitionsTheme(
builders: {
TargetPlatform.android: ZoomPageTransitionsBuilder(),
},
),
),
home: const MyFirstScreen(),
);

มีผลข้างเคียงหนึ่งอย่างที่ควรรู้ตอนเขียน widget test: transition ยาวขึ้นเป็น 450ms จากเดิม 300ms การ pump ด้วย duration ที่ hard-code ไว้จะพัง ให้ใช้ TransitionDurationObserver กับ observer.pumpPastTransition(tester) เพื่อให้ test ไม่ผูกกับความยาว animation

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: HomeScreen(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Home')),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text(
              'You are on the Home screen.',
              style: TextStyle(fontSize: 16),
            ),
            const SizedBox(height: 24),
            ElevatedButton(
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) => const DetailScreen(),
                  ),
                );
              },
              child: const Text('Go to Detail'),
            ),
          ],
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Detail')),
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text(
              'You are on the Detail screen.',
              style: TextStyle(fontSize: 16),
            ),
            const SizedBox(height: 24),
            ElevatedButton(
              onPressed: () => Navigator.pop(context),
              child: const Text('Go Back'),
            ),
          ],
        ),
      ),
    );
  }
}
Navigator.push เพิ่มอะไรเข้าไปใน Flutter navigation stack?
ทำไม Navigator.push จึงต้องการ BuildContext?
MaterialPageRoute มีหน้าที่อะไร?
Flutter จัดการปุ่มย้อนกลับ Android โดยไม่ต้องเขียน code เพิ่มเติมได้อย่างไร?