Skip to content

Kapitel 8: Routing & Navigation

8.1 Konzept der Routing (Seitennavigation)

Was ist Routing?

Routing = Mechanismus zum Wechseln zwischen Seiten (Screens) in einer App.

In Flutter:

  • Jede Seite ist ein Widget (meist StatelessWidget oder StatefulWidget)
  • Navigator verwaltet den Seiten-Stapel (Stack)
  • Seiten können vorwärts (push) und rückwärts (pop) navigieren
Navigator-Stack:
[ Seite 1 ]  ← Aktuelle Seite (oben)
[ Seite 2 ]  ← Vorherige Seite (unten)
[ Seite 3 ]  ← Noch weiter unten

8.2 Basis-Routing (Navigator)

dart
// Seite 1 (Ausgangsseite)
ElevatedButton(
  onPressed: () {
    Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => const Seite2()),
    );
  },
  child: const Text('Zu Seite 2'),
)

Erklärung:

  • Navigator.push(): Fügt neue Seite zum Stack hinzu
  • MaterialPageRoute: Animierter Übergang (von rechts für iOS, von unten für Android)
  • builder: Erstellt die neue Seite
dart
// Seite 2 (neue Seite)
ElevatedButton(
  onPressed: () {
    Navigator.pop(context);  // Entfernt aktuelle Seite vom Stack
  },
  child: const Text('Zurück'),
)

Anwendungsfall:

  • ✅ "Zurück"-Button
  • ✅ Abbrechen-Button
  • ✅ Speichern-Button (nach Speichern zurück)

Daten an vorherige Seite zurückgeben

Seite 1 (Daten erwarten):

dart
ElevatedButton(
  onPressed: () async {
    final result = await Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => const Seite2()),
    );
    
    print('Erhalten: $result');  // Verarbeitet zurückgegebene Daten
  },
  child: const Text('Zu Seite 2 (mit Rückgabe)'),
)

Seite 2 (Daten zurückgeben):

dart
ElevatedButton(
  onPressed: () {
    Navigator.pop(context, 'Hallo von Seite 2');  // Daten zurückgeben
  },
  child: const Text('Zurück mit Daten'),
)

8.3 Benannte Routing (Named Routes - empfohlen für größere Apps)

Warum benannte Routing?

Nachteil von MaterialPageRoute:

  • ❌ Seitennamen sind Strings (Tippfehler möglich)
  • ❌ Schwer zu warten bei vielen Seiten
  • ❌ Keine zentrale Verwaltung

Vorteile von benannten Routing:

  • ✅ Zentrale Verwaltung aller Routen
  • ✅ Typsicherheit (keine Tippfehler)
  • ✅ Bessere Wartbarkeit

Konfiguration (in MaterialApp)

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

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Routing Beispiel',
      
      // Routen definieren
      routes: {
        '/': (context) => const StartSeite(),
        '/profil': (context) => const ProfilSeite(),
        '/einstellungen': (context) => const EinstellungenSeite(),
      },
      
      // Startseite (initiale Route)
      initialRoute: '/',
    );
  }
}
dart
ElevatedButton(
  onPressed: () {
    Navigator.pushNamed(context, '/profil');
  },
  child: const Text('Zu Profil'),
)

Routen-Parameter übergeben & empfangen

Methode 1: Über settings (empfohlen)

dart
// In MaterialApp: onGenerateRoute verwenden
MaterialApp(
  onGenerateRoute: (settings) {
    if (settings.name == '/profil') {
      final userId = settings.arguments as String;  // Parameter empfangen
      return MaterialPageRoute(
        builder: (context) => ProfilSeite(userId: userId),
      );
    }
    return null;
  },
)

Parameter übergeben:

dart
Navigator.pushNamed(
  context,
  '/profil',
  arguments: 'user123',  // Parameter übergeben
);

Methode 2: Über ModalRoute.of() (innerhalb der Zielseite)

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

  @override
  Widget build(BuildContext context) {
    // Parameter empfangen
    final userId = ModalRoute.of(context)!.settings.arguments as String;
    
    return Scaffold(
      appBar: AppBar(title: const Text('Profil')),
      body: Center(
        child: Text('User ID: $userId'),
      ),
    );
  }
}

8.4 Routing-Wache (onGenerateRoute - für Berechtigungskontrolle)

Was ist Routing-Wache?

Anwendungsfall: Manche Seiten dürfen nur nach dem Login aufgerufen werden.

Lösung: onGenerateRoute abfangen und auf Berechtigung prüfen.

Implementierung

dart
MaterialApp(
  onGenerateRoute: (settings) {
    // Beispiel: Login-Status prüfen
    final isLoggedIn = false;  // In echt: aus Provider/SharedPreferences
    
    // Geschützte Routen
    if (settings.name == '/profil' || settings.name == '/einstellungen') {
      if (!isLoggedIn) {
        // Nicht eingeloggt → Zu Login-Seite weiterleiten
        return MaterialPageRoute(builder: (context) => const LoginSeite());
      }
    }
    
    // Normale Routing
    switch (settings.name) {
      case '/':
        return MaterialPageRoute(builder: (context) => const StartSeite());
      case '/profil':
        return MaterialPageRoute(builder: (context) => const ProfilSeite());
      case '/login':
        return MaterialPageRoute(builder: (context) => const LoginSeite());
      default:
        return MaterialPageRoute(builder: (context) => const NotFoundSeite());
    }
  },
)

8.5 Seitenübergangs-Animation (benutzerdefiniert)

Standard-Animation überschreiben

dart
Navigator.push(
  context,
  PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) => const ZielSeite(),
    transitionsBuilder: (context, animation, secondaryAnimation, child) {
      // Benutzerdefinierte Animation
      var begin = const Offset(1.0, 0.0);  // Von rechts
      var end = Offset.zero;
      var curve = Curves.ease;
      
      var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
      
      return SlideTransition(
        position: animation.drive(tween),
        child: child,
      );
    },
  ),
);

Häufige Animationen

1. Fade-Animation (Einblenden)

dart
transitionsBuilder: (context, animation, secondaryAnimation, child) {
  return FadeTransition(opacity: animation, child: child);
}

2. Scale-Animation (Vergrößern)

dart
transitionsBuilder: (context, animation, secondaryAnimation, child) {
  return ScaleTransition(scale: animation, child: child);
}

3. Rotation-Animation (Drehen)

dart
transitionsBuilder: (context, animation, secondaryAnimation, child) {
  return RotationTransition(turns: animation, child: child);
}

8.6 Praxisbeispiel: Multi-Seiten-App mit Routing

Schritt 1: Seiten erstellen

dart
// StartSeite
class StartSeite extends StatelessWidget {
  const StartSeite({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Startseite')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
              onPressed: () {
                Navigator.pushNamed(context, '/profil');
              },
              child: const Text('Zu Profil'),
            ),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: () {
                Navigator.pushNamed(context, '/einstellungen');
              },
              child: const Text('Zu Einstellungen'),
            ),
          ],
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Profil')),
      body: const Center(
        child: Text('Dies ist die Profil-Seite', style: TextStyle(fontSize: 24)),
      ),
    );
  }
}

// Einstellungen-Seite
class EinstellungenSeite extends StatelessWidget {
  const EinstellungenSeite({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Einstellungen')),
      body: const Center(
        child: Text('Dies ist die Einstellungen-Seite', style: TextStyle(fontSize: 24)),
      ),
    );
  }
}

Schritt 2: In main.dart Routen konfigurieren

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

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Routing Beispiel',
      routes: {
        '/': (context) => const StartSeite(),
        '/profil': (context) => const ProfilSeite(),
        '/einstellungen': (context) => const EinstellungenSeite(),
      },
      initialRoute: '/',
    );
  }
}

Schritt 3: App ausführen und testen

bash
flutter run

Sie sollten:

  1. Startseite sehen
  2. Auf "Zu Profil" klicken → Profil-Seite öffnet sich
  3. Auf "Zurück" (Android Back-Button oder AppBar-Pfeil) klicken → Zurück zur Startseite
  4. Auf "Zu Einstellungen" klicken → Einstellungen-Seite öffnet sich

Zusammenfassung

In diesem Kapitel haben Sie:

  • ✅ Das Konzept der Routing (Seitennavigation) verstanden
  • ✅ Basis-Routing mit Navigator.push() und Navigator.pop() gemeistert
  • ✅ Daten zwischen Seiten übergeben und empfangen
  • ✅ Benannte Routing (routes, pushNamed) für größere Apps implementiert
  • ✅ Routing-Wache (onGenerateRoute) für Berechtigungskontrolle erstellt
  • ✅ Benutzerdefinierte Seitenübergangs-Animationen erstellt
  • ✅ Ein Praxisbeispiel (Multi-Seiten-App) implementiert

Nächstes Kapitel: Wir werden Netzwerkrequests lernen (Dio, GET/POST, JSON-Parsing).


Übungsaufgaben:

  1. Erstellen Sie eine 3-Seiten-App (Start, Über uns, Kontakt) mit benannten Routing
  2. Implementieren Sie eine Login-Seite, die nach erfolgreichem Login zur Profil-Seite navigiert
  3. Übergeben Sie einen Benutzernamen von der Login-Seite zur Profil-Seite
  4. Erstellen Sie eine benutzerdefinierte Seitenübergangs-Animation (Fade oder Scale)
  5. Implementieren Sie eine Routing-Wache, die unautorisierte Zugriffe auf geschützte Seiten verhindert

Häufige Fehler:

  • Navigator.push() ohne MaterialPageRoute verwenden → Animation fehlt
  • Navigator.pop() auf der Startseite aufrufen → App schließt (oder Fehler)
  • ❌ Routen-Namen falsch schreiben (Tippfehler) → Navigator.pushNamed() findet keine Route
  • ❌ Parameter übergeben, aber in Zielseite nicht empfangen → NullPointerException

Frei für alle Anfänger