I'm attempting to add a Flutter module to a native iOS app. However, I'm having an issue where Flutter's navigation stack is maintained when presenting a FlutterViewController more than once (i.e. the details screen is shown instead of the landing page).
How do I reset the navigation stack while using a Flutter engine?
Here is the code for my demo.
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: 'example',
routes: {
'example': (context) => const LandingPage(),
},
);
}
}
class LandingPage extends StatelessWidget {
const LandingPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Landing screen')),
body: Center(
child: TextButton(
child: const Text('Go to details'),
onPressed: () => _navigateToDetails(context),
),
),
);
}
void _navigateToDetails(BuildContext context) {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => const DetailsPage()),
);
}
}
class DetailsPage extends StatelessWidget {
const DetailsPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Details screen')),
body: const Center(child: Text('Details')),
);
}
}
Here is my native Swift code.
@main
class AppDelegate: FlutterAppDelegate {
lazy var sharedEngine = FlutterEngine(name: "shared")
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
sharedEngine.run();
GeneratedPluginRegistrant.register(with: sharedEngine);
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
class ViewController: UIViewController {
@IBAction private func onButtonTapped(_ sender: UIButton) {
let page = FlutterViewController(
engine: AppDelegate.current.sharedEngine,
nibName: nil,
bundle: nil
)
present(page, animated: true)
}
}
I ended up solving this by dismissing the modal manually.
I added a close button to the
AppBar
only if running on iOS, which invokes a method on aMethodChannel
. I created a custom ViewController that will listen for calls todismiss_modal
. The key is to present this modal by settingmodalPresentationStyle
to.overCurrentContext
.