diff --git a/ch11/ex11-7 b/ch11/ex11-7 new file mode 100644 index 0000000..c3d5e4d --- /dev/null +++ b/ch11/ex11-7 @@ -0,0 +1,140 @@ +# Adding Page Navigation with Routes (Declarative) + +```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: 'Flutter Code Sample for Navigator', + // MaterialApp contains our top-level Navigator + initialRoute: '/signup', + routes: { + '/': (BuildContext context) => const HomePage(), + '/signup': (BuildContext context) => const SignUpPage(), + }, + ); + } +} + +class HomePage extends StatelessWidget { + const HomePage({super.key}); + @override + Widget build(BuildContext context) { + return DefaultTextStyle( + style: Theme.of(context).textTheme.headline4!, + child: Container( + color: Colors.white, + alignment: Alignment.center, + child: const Text('Home Page'), + ), + ); + } +} + +class SignUpPage extends StatelessWidget { + const SignUpPage({super.key}); + @override + Widget build(BuildContext context) { + // SignUpPage builds its own Navigator, which ends up being a nested + // Navigator in our app. + return Navigator( + initialRoute: 'signup/personal_info', + onGenerateRoute: (RouteSettings settings) { + WidgetBuilder builder; + switch (settings.name) { + case 'signup/personal_info': + // Assume CollectPersonalInfoPage collects personal info and then + // navigates to 'signup/choose_credentials'. + builder = (BuildContext context) => const CollectPersonalInfoPage(); + break; + case 'signup/choose_credentials': + // Assume ChooseCredentialsPage collects new credentials and then + // invokes 'onSignupComplete()'. + builder = (BuildContext _) => ChooseCredentialsPage( + onSignupComplete: () { + // Referencing Navigator.of(context) from here refers to the + // top-level Navigator because SignUpPage is above the + // nested Navigator that it created. Therefore, this pop() + // will pop the entire "sign-up" journey and return to the + // "" route, AKA HomePage. + Navigator.of(context).pop(); + }, + ); + break; + default: + throw Exception('Invalid route: ${settings.name}'); + } + return MaterialPageRoute(builder: builder, settings: settings); + }, + ); + } +} + +class ChooseCredentialsPage extends StatelessWidget { + const ChooseCredentialsPage({ + super.key, + required this.onSignupComplete, + }); + final VoidCallback onSignupComplete; + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onSignupComplete, + child: DefaultTextStyle( + style: Theme.of(context).textTheme.headline4!, + child: Container( + color: Colors.pinkAccent, + alignment: Alignment.center, + child: const Text('Choose Credentials Page'), + ), + ), + ); + } +} + +class CollectPersonalInfoPage extends StatelessWidget { + const CollectPersonalInfoPage({super.key}); + @override + Widget build(BuildContext context) { + return DefaultTextStyle( + style: Theme.of(context).textTheme.headline4!, + child: Container( + color: Colors.white, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + "Name: Max, Address: Fury Road", + style: TextStyle(fontSize: 20, color: Colors.black87), + ), + const SizedBox(height: 10.0), + GestureDetector( + onTap: () { + // This moves from the personal info page to the credentials page, + // replacing this page with that one. + Navigator.of(context) + .pushReplacementNamed('signup/choose_credentials'); + }, + child: const SizedBox( + child: Text( + 'Link: Page', + style: TextStyle( + fontSize: 20, + color: Colors.blue, + decoration: TextDecoration.underline), + ), + ), + ), + ], + ), + ), + ); + } +} + +```