Justin McCandless dedd100ebd
Predictive back support for root routes (#120385)
This PR aims to support Android's predictive back gesture when popping the entire Flutter app.  Predictive route transitions between routes inside of a Flutter app will come later.

<img width="200" src="https://user-images.githubusercontent.com/389558/217918109-945febaa-9086-41cc-a476-1a189c7831d8.gif" />

### Trying it out

If you want to try this feature yourself, here are the necessary steps:

  1. Run Android 33 or above.
  1. Enable the feature flag for predictive back on the device under "Developer
     options".
  1. Create a Flutter project, or clone [my example project](https://github.com/justinmc/flutter_predictive_back_examples).
  1. Set `android:enableOnBackInvokedCallback="true"` in
     android/app/src/main/AndroidManifest.xml (already done in the example project).
  1. Check out this branch.
  1. Run the app. Perform a back gesture (swipe from the left side of the
     screen).

You should see the predictive back animation like in the animation above and be able to commit or cancel it.

### go_router support

go_router works with predictive back out of the box because it uses a Navigator internally that dispatches NavigationNotifications!

~~go_router can be supported by adding a listener to the router and updating SystemNavigator.setFrameworkHandlesBack.~~

Similar to with nested Navigators, nested go_routers is supported by using a PopScope widget.

<details>

<summary>Full example of nested go_routers</summary>

```dart
// 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.

import 'package:go_router/go_router.dart';

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

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

class _MyApp extends StatelessWidget {
  final GoRouter router = GoRouter(
    routes: <RouteBase>[
      GoRoute(
        path: '/',
        builder: (BuildContext context, GoRouterState state) => _HomePage(),
      ),
      GoRoute(
        path: '/nested_navigators',
        builder: (BuildContext context, GoRouterState state) => _NestedGoRoutersPage(),
      ),
    ],
  );

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: router,
    );
  }
}

class _HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Nested Navigators Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('Home Page'),
            const Text('A system back gesture here will exit the app.'),
            const SizedBox(height: 20.0),
            ListTile(
              title: const Text('Nested go_router route'),
              subtitle: const Text('This route has another go_router in addition to the one used with MaterialApp above.'),
              onTap: () {
                context.push('/nested_navigators');
              },
            ),
          ],
        ),
      ),
    );
  }
}

class _NestedGoRoutersPage extends StatefulWidget {
  @override
  State<_NestedGoRoutersPage> createState() => _NestedGoRoutersPageState();
}

class _NestedGoRoutersPageState extends State<_NestedGoRoutersPage> {
  late final GoRouter _router;
  final GlobalKey<NavigatorState> _nestedNavigatorKey = GlobalKey<NavigatorState>();

  // If the nested navigator has routes that can be popped, then we want to
  // block the root navigator from handling the pop so that the nested navigator
  // can handle it instead.
  bool get _popEnabled {
    // canPop will throw an error if called before build. Is this the best way
    // to avoid that?
    return _nestedNavigatorKey.currentState == null ? true : !_router.canPop();
  }

  void _onRouterChanged() {
    // Here the _router reports the location correctly, but canPop is still out
    // of date.  Hence the post frame callback.
    SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
      setState(() {});
    });
  }

  @override
  void initState() {
    super.initState();

    final BuildContext rootContext = context;
    _router = GoRouter(
      navigatorKey: _nestedNavigatorKey,
      routes: [
        GoRoute(
          path: '/',
          builder: (BuildContext context, GoRouterState state) => _LinksPage(
            title: 'Nested once - home route',
            backgroundColor: Colors.indigo,
            onBack: () {
              rootContext.pop();
            },
            buttons: <Widget>[
              TextButton(
                onPressed: () {
                  context.push('/two');
                },
                child: const Text('Go to another route in this nested Navigator'),
              ),
            ],
          ),
        ),
        GoRoute(
          path: '/two',
          builder: (BuildContext context, GoRouterState state) => _LinksPage(
            backgroundColor: Colors.indigo.withBlue(255),
            title: 'Nested once - page two',
          ),
        ),
      ],
    );

    _router.addListener(_onRouterChanged);
  }

  @override
  void dispose() {
    _router.removeListener(_onRouterChanged);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return PopScope(
      popEnabled: _popEnabled,
      onPopped: (bool success) {
        if (success) {
          return;
        }
        _router.pop();
      },
      child: Router<Object>.withConfig(
        restorationScopeId: 'router-2',
        config: _router,
      ),
    );
  }
}

class _LinksPage extends StatelessWidget {
  const _LinksPage ({
    required this.backgroundColor,
    this.buttons = const <Widget>[],
    this.onBack,
    required this.title,
  });

  final Color backgroundColor;
  final List<Widget> buttons;
  final VoidCallback? onBack;
  final String title;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: backgroundColor,
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(title),
            //const Text('A system back here will go back to Nested Navigators Page One'),
            ...buttons,
            TextButton(
              onPressed: onBack ?? () {
                context.pop();
              },
              child: const Text('Go back'),
            ),
          ],
        ),
      ),
    );
  }
}
```

</details>

### Resources

Fixes https://github.com/flutter/flutter/issues/109513
Depends on engine PR https://github.com/flutter/engine/pull/39208 ✔️ 
Design doc: https://docs.google.com/document/d/1BGCWy1_LRrXEB6qeqTAKlk-U2CZlKJ5xI97g45U7azk/edit#
Migration guide: https://github.com/flutter/website/pull/8952
2023-08-04 20:44:44 +00:00
2023-07-25 22:49:45 +00:00
2019-11-27 15:04:02 -08:00
2019-11-27 15:04:02 -08:00
2023-05-03 20:01:34 +00:00

Flutter

Build Status - Cirrus Discord badge Twitter handle codecov CII Best Practices OpenSSF Scorecard SLSA 1

Flutter is Google's SDK for crafting beautiful, fast user experiences for mobile, web, and desktop from a single codebase. Flutter works with existing code, is used by developers and organizations around the world, and is free and open source.

Documentation

For announcements about new releases, follow the flutter-announce@googlegroups.com mailing list. Our documentation also tracks breaking changes across releases.

Terms of service

The Flutter tool may occasionally download resources from Google servers. By downloading or using the Flutter SDK, you agree to the Google Terms of Service: https://policies.google.com/terms

For example, when installed from GitHub (as opposed to from a prepackaged archive), the Flutter tool will download the Dart SDK from Google servers immediately when first run, as it is used to execute the flutter tool itself. This will also occur when Flutter is upgraded (e.g. by running the flutter upgrade command).

About Flutter

We think Flutter will help you create beautiful, fast apps, with a productive, extensible and open development model, whether you're targeting iOS or Android, web, Windows, macOS, Linux or embedding it as the UI toolkit for a platform of your choice.

Beautiful user experiences

We want to enable designers to deliver their full creative vision without being forced to water it down due to limitations of the underlying framework. Flutter's layered architecture gives you control over every pixel on the screen and its powerful compositing capabilities let you overlay and animate graphics, video, text, and controls without limitation. Flutter includes a full set of widgets that deliver pixel-perfect experiences whether you're building for iOS (Cupertino) or other platforms (Material), along with support for customizing or creating entirely new visual components.

Reflectly hero image

Fast results

Flutter is fast. It's powered by the same hardware-accelerated 2D graphics library that underpins Chrome and Android: Skia. We architected Flutter to support glitch-free, jank-free graphics at the native speed of your device. Flutter code is powered by the world-class Dart platform, which enables compilation to 32-bit and 64-bit ARM machine code for iOS and Android, as well as JavaScript for the web and Intel x64 for desktop devices.

Dart diagram

Productive development

Flutter offers stateful hot reload, allowing you to make changes to your code and see the results instantly without restarting your app or losing its state.

Hot reload animation

Extensible and open model

Flutter works with any development tool (or none at all), and also includes editor plug-ins for both Visual Studio Code and IntelliJ / Android Studio. Flutter provides tens of thousands of packages to speed your development, regardless of your target platform. And accessing other native code is easy, with support for both FFI (on Android, on iOS, on macOS, and on Windows) as well as platform-specific APIs.

Flutter is a fully open-source project, and we welcome contributions. Information on how to get started can be found in our contributor guide.

Description
A vendored version of the flutter engine for firka :3
Readme BSD-3-Clause 322 MiB
Languages
Dart 75.4%
C++ 16.4%
Objective-C++ 2.7%
Java 2.7%
Objective-C 0.6%
Other 1.8%