Files
flutter-study-notebook/study_guide/Flutter Navigation and Routing.md
T

6.2 KiB

Flutter Navigation and Routing: A Comprehensive Guide

Navigation and routing are essential for creating multi-screen applications in Flutter. This guide covers both Navigator 1.0 and 2.0 approaches, as well as named routes and generated routes.

1. Navigator 1.0

Navigator 1.0 is the traditional way of handling navigation in Flutter.

Basic Navigation

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: FirstScreen(),
    );
  }
}

class FirstScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('First Screen')),
      body: Center(
        child: ElevatedButton(
          child: Text('Go to Second Screen'),
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => SecondScreen()),
            );
          },
        ),
      ),
    );
  }
}

class SecondScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Second Screen')),
      body: Center(
        child: ElevatedButton(
          child: Text('Go back'),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
      ),
    );
  }
}

Named Routes

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: '/',
      routes: {
        '/': (context) => HomeScreen(),
        '/details': (context) => DetailsScreen(),
      },
    );
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Home')),
      body: Center(
        child: ElevatedButton(
          child: Text('Go to Details'),
          onPressed: () {
            Navigator.pushNamed(context, '/details');
          },
        ),
      ),
    );
  }
}

class DetailsScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Details')),
      body: Center(
        child: ElevatedButton(
          child: Text('Go back'),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
      ),
    );
  }
}

2. Navigator 2.0

Navigator 2.0 provides a more declarative approach to navigation.

Example:

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final RouteInformationParser<List<String>> _routeInformationParser = MyRouteInformationParser();
  final RouterDelegate<List<String>> _routerDelegate = MyRouterDelegate();

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      title: 'Navigator 2.0 Demo',
      routerDelegate: _routerDelegate,
      routeInformationParser: _routeInformationParser,
    );
  }
}

class MyRouteInformationParser extends RouteInformationParser<List<String>> {
  @override
  Future<List<String>> parseRouteInformation(RouteInformation routeInformation) async {
    final uri = Uri.parse(routeInformation.location!);
    return uri.pathSegments;
  }

  @override
  RouteInformation restoreRouteInformation(List<String> configuration) {
    return RouteInformation(location: '/' + configuration.join('/'));
  }
}

class MyRouterDelegate extends RouterDelegate<List<String>> 
    with ChangeNotifier, PopNavigatorRouterDelegateMixin<List<String>> {
  final List<String> _stack = [''];
  @override
  final navigatorKey = GlobalKey<NavigatorState>();

  List<String> get stack => List.unmodifiable(_stack);

  @override
  Widget build(BuildContext context) {
    return Navigator(
      key: navigatorKey,
      pages: [
        MaterialPage(
          key: ValueKey('HomePage'),
          child: HomePage(
            onPush: (String value) {
              _stack.add(value);
              notifyListeners();
            },
          ),
        ),
        if (_stack.length > 1)
          MaterialPage(
            key: ValueKey('DetailsPage'),
            child: DetailsPage(
              data: _stack.last,
              onPop: () {
                _stack.removeLast();
                notifyListeners();
              },
            ),
          ),
      ],
      onPopPage: (route, result) {
        if (!route.didPop(result)) {
          return false;
        }
        if (_stack.length > 1) {
          _stack.removeLast();
          notifyListeners();
        }
        return true;
      },
    );
  }

  @override
  Future<void> setNewRoutePath(List<String> configuration) async {
    _stack
      ..clear()
      ..addAll(configuration);
    notifyListeners();
  }
}

class HomePage extends StatelessWidget {
  final void Function(String) onPush;

  HomePage({required this.onPush});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Home')),
      body: Center(
        child: ElevatedButton(
          child: Text('Go to Details'),
          onPressed: () => onPush('details'),
        ),
      ),
    );
  }
}

class DetailsPage extends StatelessWidget {
  final String data;
  final VoidCallback onPop;

  DetailsPage({required this.data, required this.onPop});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Details')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Details for: $data'),
            ElevatedButton(
              child: Text('Go back'),
              onPressed: onPop,
            ),
          ],
        ),
      ),
    );
  }
}

Conclusion

Understanding both Navigator 1.0 and 2.0 approaches is crucial for effective navigation in Flutter applications. While Navigator 1.0 is simpler and sufficient for many apps, Navigator 2.0 offers more control and is especially useful for web applications and complex navigation scenarios.