flutter_navigation_generator 2.0.1 copy "flutter_navigation_generator: ^2.0.1" to clipboard
flutter_navigation_generator: ^2.0.1 copied to clipboard

Generate your Flutter navigator by annotating your screen widgets

example/lib/main.dart

import 'package:example/custom_model.dart';
import 'package:example/example.dart';
import 'package:example/example.navigator.dart';
import 'package:example/fade_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_navigation_generator_annotations/flutter_navigation_generator_annotations.dart';

Future<void> main() async {
  await mainNavigator.updateGuards();
  runApp(const MyApp());
}

final mainNavigator = MainNavigator();

var globalStateIsLoggedIn = false;

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      onGenerateRoute: mainNavigator.onGenerateRoute,
      navigatorKey: mainNavigator.navigatorKey,
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

@FlutterRoute(
  routeName: '/',
)
@FlutterRoute(
  methodName: 'goToHomePageWithPathParameter',
  routeName: 'my-home-page-pop-all/:title',
  navigationType: NavigationType.pushAndReplaceAll,
)
class MyHomePage extends StatefulWidget {
  const MyHomePage({
    super.key,
    this.title,
  });

  @FlutterRouteConstructor(
    routeName: 'MyHomePagePopAll',
  )
  const MyHomePage.popAll({super.key, this.title = 'Popped all pages'});

  final String? title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool? _result;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: widget.title == null
          ? null
          : AppBar(
              backgroundColor: Theme.of(context).colorScheme.inversePrimary,
              title: Text(widget.title!),
            ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              'Result from page 2: $_result',
            ),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Checkbox(
                  value: globalStateIsLoggedIn,
                  onChanged: (value) {
                    globalStateIsLoggedIn = value!;
                    setState(() {});
                    mainNavigator.updateGuard<LoginGuard>();
                  },
                ),
                const Text(
                  'Is user logged in?',
                ),
                const SizedBox(width: 8),
                ElevatedButton(
                  onPressed: () => mainNavigator.goToLoggedInPage(),
                  child: const Text("Go to logged in page"),
                ),
              ],
            ),
            ElevatedButton(
              onPressed: () async {
                _result = await mainNavigator.goToSecondPage();
                setState(() {});
              },
              child: const Text("Go to page 2 (with fade animation)"),
            ),
            ElevatedButton(
              onPressed: () => mainNavigator.goToExampleScreenWithRequiredArgument(data: [CustomModel('John', 25), CustomModel('Jeff', 27)]),
              child: const Text("Go to ExampleScreenWithRequiredArgument"),
            ),
            ElevatedButton(
              onPressed: () => mainNavigator.customName(id: '1', name: 'John', age: 12),
              child: const Text("Go to RouteNameWithArguments"),
            ),
            ElevatedButton(
              onPressed: () => mainNavigator.goToRouteNameWithArguments2(id: '3', name: 'Will', age: 43),
              child: const Text("Go to RouteNameWithArguments2"),
            ),
            ElevatedButton(
              onPressed: () => mainNavigator.showSheetRecursiveNavigationBottomSheet(),
              child: const Text("Show a bottom sheet with its own navigator"),
            ),
            ElevatedButton(
              onPressed: () => mainNavigator.showDialogExampleDialog(text: 'hi there'),
              child: const Text("Show a full screen dialog"),
            ),
            Text("Has a navigation blocked by a guard (not logged in): ${mainNavigator.canContinueNavigation()}"),
            if (mainNavigator.canContinueNavigation()) ...[
              ElevatedButton(
                onPressed: () async {
                  globalStateIsLoggedIn = true;
                  await mainNavigator.updateGuard<LoginGuard>();
                  mainNavigator.continueNavigation();
                },
                child: const Text("Continue navigation (and set logged in)"),
              ),
            ],
          ],
        ),
      ),
    );
  }
}

@FlutterRoute(
  returnType: bool,
  pageType: FadeInRoute,
)
@FlutterRoute(
  // If you don't specify another routeName, make sure the returnType and pagetype are the same
  navigationType: NavigationType.popAndPush,
  returnType: bool,
  pageType: FadeInRoute,
  methodName: 'popAndGoToSecondPage',
)
class SecondPage extends StatelessWidget {
  const SecondPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('second page'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              'Second page :raised_hands:',
            ),
            ElevatedButton(
              onPressed: () => mainNavigator.goBackWithResult(result: true),
              child: const Text("I return true"),
            ),
            ElevatedButton(
              onPressed: mainNavigator.goBack,
              child: const Text("I return nothing"),
            ),
          ],
        ),
      ),
    );
  }
}

@FlutterRoute(
  routeName: 'home/:id/:name/:nonExistingName/',
  methodName: 'customName',
)
class RouteNameWithArguments extends StatelessWidget {
  final String id;
  final String? name;
  final int? age;

  const RouteNameWithArguments({
    required this.id,
    this.name,
    this.age,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('RouteNameWithArguments'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              'RouteNameWithArguments:',
            ),
            Text(
              'id: $id',
            ),
            Text(
              'name: $name',
            ),
            Text(
              'age: $age',
            ),
            ElevatedButton(
              onPressed: mainNavigator.goBack,
              child: const Text("Back"),
            ),
          ],
        ),
      ),
    );
  }
}

@FlutterRoute(
  routeName: '/home/:id/example/:age',
)
class RouteNameWithArguments2 extends StatelessWidget {
  final String id;
  final String? name;
  final int? age;

  const RouteNameWithArguments2({
    required this.id,
    this.name,
    this.age,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('RouteNameWithArguments2'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              'RouteNameWithArguments2:',
            ),
            Text(
              'id: $id',
            ),
            Text(
              'name: $name',
            ),
            Text(
              'age: $age',
            ),
            ElevatedButton(
              onPressed: mainNavigator.goBack,
              child: const Text("Back"),
            ),
          ],
        ),
      ),
    );
  }
}

@FlutterRoute(
  navigationType: NavigationType.bottomSheet,
)
class RecursiveNavigationBottomSheet extends StatelessWidget {
  final myNavigator = MainNavigator();
  final int layers;

  RecursiveNavigationBottomSheet({
    this.layers = 1,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return BottomSheet(
      onClosing: mainNavigator.goBack,
      builder: (context) => Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text(
            'layer $layers',
          ),
          const Text(
            'I am a bottom sheet with my own navigator',
          ),
          Wrap(
            children: [
              ElevatedButton(
                onPressed: () => myNavigator.goToHomePageWithPathParameter(),
                child: const Text("Pop all and show home page"),
              ),
              ElevatedButton(
                onPressed: () => myNavigator.goToSecondPage(),
                child: const Text("Go to second page"),
              ),
              ElevatedButton(
                onPressed: () => myNavigator.showSheetRecursiveNavigationBottomSheet(layers: layers + 1),
                child: const Text("Open another bottom sheet"),
              ),
            ],
          ),
          Expanded(
            child: Navigator(
              key: myNavigator.navigatorKey,
              onGenerateRoute: myNavigator.onGenerateRoute,
              initialRoute: RouteNames.myHomePage,
            ),
          ),
        ],
      ),
    );
  }
}

@flutterDialog
class ExampleDialog extends StatelessWidget {
  final String text;

  const ExampleDialog({
    required this.text,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Dialog(
      child: Center(
        child: Text(
          text,
        ),
      ),
    );
  }
}

@flutterRoute
class ExampleScreenWithRequiredArgument extends StatelessWidget {
  final List<CustomModel> data;

  const ExampleScreenWithRequiredArgument({
    required this.data,
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              'Got ${data.length} items:',
              style: const TextStyle(fontSize: 40),
            ),
            for (final item in data) ...[
              Text(
                item.name,
                style: const TextStyle(fontSize: 40),
              ),
              Text(
                item.age.toString(),
                style: const TextStyle(fontSize: 40),
              ),
            ],
            ElevatedButton(
              onPressed: mainNavigator.goBack,
              child: const Text("Back"),
            ),
          ],
        ),
      ),
    );
  }
}

@FlutterRoute(
  routeName: '404',
  generateMethod: false,
)
class Error404 extends StatelessWidget {
  const Error404({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('404 not found'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              'We couldn\'t find this page, sorry :(',
            ),
            ElevatedButton(
              onPressed: () => mainNavigator.goToMyHomePage(title: 'returning from 404'),
              child: const Text("go home"),
            ),
          ],
        ),
      ),
    );
  }
}

@FlutterRoute(
  generateMethod: false,
)
class ErrorNotLoggedIn extends StatelessWidget {
  const ErrorNotLoggedIn({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('You are not logged in'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              'You are not logged in, sorry :(',
            ),
            ElevatedButton(
              onPressed: () => mainNavigator.goToMyHomePage(title: 'returning from not logged in'),
              child: const Text("go home"),
            ),
            ElevatedButton(
              onPressed: () async {
                globalStateIsLoggedIn = true;
                await mainNavigator.updateGuard<LoginGuard>();
                mainNavigator.continueNavigation();
              },
              child: const Text("Continue navigation (and set logged in)"),
            ),
          ],
        ),
      ),
    );
  }
}

@FlutterRoute(
  guards: [
    LoginGuard,
  ],
)
class LoggedInPage extends StatelessWidget {
  const LoggedInPage({
    super.key,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('You are logged in'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              'You are logged in, yay :)',
            ),
            ElevatedButton(
              onPressed: () => mainNavigator.goToMyHomePage(title: 'returning from logged in'),
              child: const Text("go home"),
            ),
          ],
        ),
      ),
    );
  }
}

class LoginGuard extends NavigatorGuard {
  LoginGuard() : super(RouteNames.errorNotLoggedIn);

  @override
  Future<bool> getValue() async => globalStateIsLoggedIn;
}

class ExampleDefaultGuard extends NavigatorGuard {
  ExampleDefaultGuard() : super(RouteNames.r404);

  @override
  Future<bool> getValue() async => true;
}