// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This sample demonstrates how to use a PopScope to wrap a widget that
// may pop the page with a result.

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: '/home',
      onGenerateRoute: (RouteSettings settings) {
        return switch (settings.name) {
          '/two' => MaterialPageRoute<FormData>(
            builder: (BuildContext context) => const _PageTwo(),
          ),
          _ => MaterialPageRoute<void>(
            builder: (BuildContext context) => const _HomePage(),
          ),
        };
      },
    );
  }
}

class _HomePage extends StatefulWidget {
  const _HomePage();

  @override
  State<_HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<_HomePage> {
  FormData? _formData;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('Page One'),
            if (_formData != null)
              Text('Hello ${_formData!.name}, whose favorite food is ${_formData!.favoriteFood}.'),
            TextButton(
              onPressed: () async {
                final FormData formData =
                    await Navigator.of(context).pushNamed<FormData?>('/two')
                        ?? const FormData();
                if (formData != _formData) {
                  setState(() {
                    _formData = formData;
                  });
                }
              },
              child: const Text('Next page'),
            ),
          ],
        ),
      ),
    );
  }
}

class _PopScopeWrapper extends StatelessWidget {
  const _PopScopeWrapper({required this.child});

  final Widget child;

  Future<bool?> _showBackDialog(BuildContext context) {
    return showDialog<bool>(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text('Are you sure?'),
          content: const Text(
            'Are you sure you want to leave this page?',
          ),
          actions: <Widget>[
            TextButton(
              style: TextButton.styleFrom(
                textStyle: Theme.of(context).textTheme.labelLarge,
              ),
              child: const Text('Never mind'),
              onPressed: () {
                Navigator.pop(context, false);
              },
            ),
            TextButton(
              style: TextButton.styleFrom(
                textStyle: Theme.of(context).textTheme.labelLarge,
              ),
              child: const Text('Leave'),
              onPressed: () {
                Navigator.pop(context, true);
              },
            ),
          ],
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return PopScope<FormData>(
      canPop: false,
      // The result argument contains the pop result that is defined in `_PageTwo`.
      onPopInvokedWithResult: (bool didPop, FormData? result) async {
        if (didPop) {
          return;
        }
        final bool shouldPop = await _showBackDialog(context) ?? false;
        if (context.mounted && shouldPop) {
          Navigator.pop(context, result);
        }
      },
      child: child,
    );
  }
}

// This is a PopScope wrapper over _PageTwoBody
class _PageTwo extends StatelessWidget {
  const _PageTwo();

  @override
  Widget build(BuildContext context) {
    return const _PopScopeWrapper(
      child: _PageTwoBody(),
    );
  }

}

class _PageTwoBody extends StatefulWidget {
  const _PageTwoBody();

  @override
  State<_PageTwoBody> createState() => _PageTwoBodyState();
}

class _PageTwoBodyState extends State<_PageTwoBody> {
  FormData _formData = const FormData();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('Page Two'),
            Form(
              child: Column(
                children: <Widget>[
                  TextFormField(
                    decoration: const InputDecoration(
                      hintText: 'Enter your name.',
                    ),
                    onChanged: (String value) {
                      _formData = _formData.copyWith(
                        name: value,
                      );
                    },
                  ),
                  TextFormField(
                    decoration: const InputDecoration(
                      hintText: 'Enter your favorite food.',
                    ),
                    onChanged: (String value) {
                      _formData = _formData.copyWith(
                        favoriteFood: value,
                      );
                    },
                  ),
                ],
              ),
            ),
            TextButton(
              onPressed: () async {
                Navigator.maybePop(context, _formData);
              },
              child: const Text('Go back'),
            ),
          ],
        ),
      ),
    );
  }
}

@immutable
class FormData {
  const FormData({
    this.name = '',
    this.favoriteFood = '',
  });

  final String name;
  final String favoriteFood;

  FormData copyWith({String? name, String? favoriteFood}) {
    return FormData(
      name: name ?? this.name,
      favoriteFood: favoriteFood ?? this.favoriteFood,
    );
  }

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) {
      return true;
    }
    if (other.runtimeType != runtimeType) {
      return false;
    }
    return other is FormData
        && other.name == name
        && other.favoriteFood == favoriteFood;
  }

  @override
  int get hashCode => Object.hash(name, favoriteFood);
}
