Skip to content

[iOS] Back gesture issue with concurrent manual and programmatic pop #141268

@maRci002

Description

@maRci002

Steps to reproduce

  • Implement a Flutter app with navigation using CupertinoPageTransitionsBuilder or test on iOS.
  • Set up a scenario where a route can be manually dragged to close (e.g., swiping to go back).
  • Introduce a condition where Navigator.pop or Navigator.maybePop is called programmatically during the manual drag. This could be triggered by an event like a timer expiration, response to deeplinking, etc.
  • Attempt to manually drag to close the route while the programmatic pop is being called.

Expected results

The route that is being manually dragged should be successfully popped off the navigation stack.

Actual results

  • The route that is being dragged does not pop as expected.
  • Subsequent calls to Navigator.pop or Navigator.maybePop do not function properly.
  • Popping the route using gesture still works, but results in a black screen.

Code sample

Code sample
import 'dart:async';

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

class AppScrollBehavior extends MaterialScrollBehavior {
  const AppScrollBehavior();

  @override
  Set<PointerDeviceKind> get dragDevices => const {...PointerDeviceKind.values};
}

void main() {
  runApp(const MainApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        appBarTheme: const AppBarTheme(centerTitle: true),
        pageTransitionsTheme: PageTransitionsTheme(
          builders: {
            for (final platform in TargetPlatform.values)
              platform: const CupertinoPageTransitionsBuilder(),
          },
        ),
      ),
      scrollBehavior: const AppScrollBehavior(),
      home: const ScreenOne(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Screen One'),
      ),
      body: Center(
        child: TextButton(
          onPressed: () {
            Navigator.of(context).push(
              MaterialPageRoute(builder: (_) => const ScreenTwo()),
            );
          },
          child: const Text('Go to Screen Two'),
        ),
      ),
    );
  }
}

class ScreenTwo extends StatefulWidget {
  const ScreenTwo({super.key});

  @override
  State<ScreenTwo> createState() => _ScreenTwoState();
}

class _ScreenTwoState extends State<ScreenTwo> {
  int seconds = 5;
  late Timer timer;

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

    timer = Timer.periodic(
      const Duration(seconds: 1),
      (Timer timer) {
        setState(() => seconds--);

        if (seconds == 0) {
          timer.cancel();
          pop();
        }
      },
    );
  }

  @override
  void dispose() {
    timer.cancel();
    super.dispose();
  }

  void pop() {
    Navigator.of(context).pop();
  }

  void maybePop() {
    Navigator.of(context).maybePop();
  }

  void navigate() {
    Navigator.of(context).push(
      MaterialPageRoute(builder: (_) => const ScreenThree()),
    );
  }

  @override
  Widget build(BuildContext context) {
    return PopScope(
      canPop: true,
      onPopInvoked: (didPop) => print('ScreenTwo onPopInvoked didPop? $didPop'),
      child: Scaffold(
        appBar: AppBar(
          title: const Text('Screen Two'),
        ),
        body: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              Text('Auto pop in: $seconds'),
              const SizedBox(height: 8),
              TextButton(
                onPressed: pop,
                child: const Text('Pop'),
              ),
              const SizedBox(height: 8),
              TextButton(
                onPressed: maybePop,
                child: const Text('Maybe Pop'),
              ),
              const SizedBox(height: 8),
              TextButton(
                onPressed: navigate,
                child: const Text('Go to Screen Three'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return PopScope(
      canPop: true,
      onPopInvoked: (didPop) =>
          print('ScreenThree onPopInvoked didPop? $didPop'),
      child: Scaffold(
        appBar: AppBar(
          title: const Text('Screen Three'),
        ),
        body: const Center(
          child: Text('Hello World!'),
        ),
      ),
    );
  }
}

Screenshots or Video

Screenshots / Video demonstration
ios.back.gesture.bug.mp4

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
[√] Flutter (Channel stable, 3.16.5, on Microsoft Windows [Version 10.0.19045.3930], locale hu-HU)
    • Flutter version 3.16.5 on channel stable at C:\flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 78666c8dc5 (3 weeks ago), 2023-12-19 16:14:14 -0800
    • Engine revision 3f3e560236
    • Dart version 3.2.3
    • DevTools version 2.28.4

[√] Windows Version (Installed version of Windows is version 10 or higher)

[√] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    • Android SDK at C:\Users\maRci002\AppData\Local\Android\Sdk
    • Platform android-34, build-tools 34.0.0
    • ANDROID_HOME = C:\Users\maRci002\AppData\Local\Android\Sdk
    • ANDROID_SDK_ROOT = C:\Users\maRci002\AppData\Local\Android\Sdk
    • Java binary at: C:\Program Files\Android\Android Studio\jbr\bin\java
    • Java version OpenJDK Runtime Environment (build 17.0.7+0-b2043.56-10550314)
    • All Android licenses accepted.

[√] Chrome - develop for the web
    • Chrome at C:\Program Files\Google\Chrome\Application\chrome.exe

[√] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.7.0)
    • Visual Studio at C:\Program Files\Microsoft Visual Studio\2022\Community
    • Visual Studio Community 2022 version 17.7.34003.232
    • Windows 10 SDK version 10.0.22621.0

[√] Android Studio (version 2023.1)
    • Android Studio at C:\Program Files\Android\Android Studio
    • Flutter plugin can be installed from:
       https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
       https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 17.0.7+0-b2043.56-10550314)

[√] VS Code (version 1.85.1)
    • VS Code at C:\Users\maRci002\AppData\Local\Programs\Microsoft VS Code
    • Flutter extension can be installed from:
       https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter

[√] Connected device (3 available)
    • Windows (desktop) • windows • windows-x64    • Microsoft Windows [Version 10.0.19045.3930]
    • Chrome (web)      • chrome  • web-javascript • Google Chrome 120.0.6099.201
    • Edge (web)        • edge    • web-javascript • Microsoft Edge 120.0.2210.121

[√] Network resources
    • All expected network resources are available.

• No issues found!

Metadata

Metadata

Assignees

Labels

P2Important issues not at the top of the work listf: cupertinoflutter/packages/flutter/cupertino repositoryf: routesNavigator, Router, and related APIs.found in release: 3.16Found to occur in 3.16found in release: 3.19Found to occur in 3.19frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onplatform-iosiOS applications specificallyr: fixedIssue is closed as already fixed in a newer versionteam-designOwned by Design Languages teamtriaged-designTriaged by Design Languages team

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions