Repository navigation
Description
What package does this bug report belong to?
go_router
What target platforms are you seeing this bug on?
iOS and macOS are confirmed. The fallback to NoTransitionPage is platform-independent.
Have you already upgraded your packages?
Yes.
Dependency versions
Relevant pubspec.lock entries
cupertino_ui:
dependency: transitive
source: hosted
version: "1.0.0"
go_router:
dependency: "direct main"
source: hosted
version: "17.5.0"
material_ui:
dependency: "direct main"
source: hosted
version: "1.0.0"
Steps to reproduce
- Run
flutter create --empty bugwith Flutter 3.47.0. - Add
go_router: ^17.5.0andmaterial_ui: ^1.0.0. - Replace
lib/main.dartwith the code sample below. - Run the app and tap Open details.
- Tap Back.
The sample forces TargetPlatform.iOS, so the expected Cupertino slide can be checked on any host. Removing that override reproduces the same missing slide on an iOS or macOS target.
Expected results
Routes declared with GoRoute.builder under material_ui.MaterialApp.router should receive the same platform-adaptive default page as routes under the legacy SDK MaterialApp.router. In this sample, pushing and popping Details should use the Cupertino horizontal slide.
Actual results
The route changes instantly with no page transition.
go_router imports MaterialApp from package:flutter/material.dart and checks it with findAncestorWidgetOfExactType<MaterialApp>(). A package:material_ui/material_ui.dart MaterialApp is a different type, so the check returns false. GoRouter then classifies the app as a WidgetsApp and wraps every route using builder in NoTransitionPage:
- https://github.com/flutter/packages/blob/main/packages/go_router/lib/src/pages/material.dart#L7-L13
- https://github.com/flutter/packages/blob/main/packages/go_router/lib/src/builder.dart#L344-L377
A widget test against the sample confirms that ModalRoute.of(detailsContext).settings is a NoTransitionPage<void>.
The workaround is to replace every GoRoute.builder and ShellRoute.builder with a pageBuilder that explicitly returns material_ui.MaterialPage. This restores the configured platform transition, but requires every route to opt out of GoRouter's normal page selection.
I found the general decoupling tracker (#101479) and the proposal for a configurable default page builder (#113282), but neither reports this standalone MaterialApp detection regression.
Code sample
Code sample
import 'package:go_router/go_router.dart';
import 'package:material_ui/material_ui.dart';
void main() => runApp(const MainApp());
final router = GoRouter(
debugLogDiagnostics: true,
routes: [
GoRoute(
path: '/',
builder: (context, state) => Scaffold(
appBar: AppBar(title: const Text('Home')),
body: Center(
child: ElevatedButton(
onPressed: () => context.push('/details'),
child: const Text('Open details'),
),
),
),
),
GoRoute(
path: '/details',
builder: (context, state) => Scaffold(
appBar: AppBar(title: const Text('Details')),
body: Center(
child: ElevatedButton(
onPressed: context.pop,
child: const Text('Back'),
),
),
),
),
],
);
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
theme: ThemeData(platform: TargetPlatform.iOS),
routerConfig: router,
);
}
}Screenshots or Videos
Not included; the route changes instantly rather than animating.
Logs
There is no exception. The minimal sample analyzes successfully, and this test demonstrates the selected page type:
testWidgets('builder route falls back to NoTransitionPage', (tester) async {
await tester.pumpWidget(const MainApp());
await tester.pumpAndSettle();
await tester.tap(find.text('Open details'));
await tester.pump();
final route = ModalRoute.of(tester.element(find.text('Details')));
expect(route?.settings, isA<NoTransitionPage<void>>());
});00:00 +0: builder route falls back to NoTransitionPage
00:00 +1: All tests passed!Flutter Doctor output
Doctor output
[✓] Flutter (Channel stable, 3.47.0, on macOS 26.6.1 25G76 darwin-arm64, locale en-US)
• Flutter version 3.47.0 on channel stable
• Framework revision 4cf2416426 (2026-08-11)
• Engine revision 5f77625673
• Dart version 3.13.0
• DevTools version 2.60.0
• Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android, enable-ios, cli-animations, enable-native-assets, enable-record-use, enable-swift-package-manager, omit-legacy-version-file, enable-lldb-debugging, enable-uiscene-migration
[✓] Android toolchain - develop for Android devices (Android SDK version 36.0.0)
• Platform android-36, build-tools 36.0.0
• Java version OpenJDK Runtime Environment (build 21.0.9)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 26.6)
• Build 17F113
• CocoaPods version 1.17.0
[✓] Chrome - develop for the web
• Chrome 151.0.7922.138
[✓] Connected devices
• iOS physical devices (identifiers redacted)
• macOS 26.6.1
• Chrome
[✓] Network resources
• No issues found!
Activity
KCO-SK commentedon Aug 14, 2026
Confirming this on Flutter 3.47.0 / material_ui 1.0.0 / go_router 17.5.0, and I'd like to add a consequence that I think makes this more than a cosmetic regression: the Duration.zero fallback silently disables every Hero animation in the app.
That is how it surfaced for us. We migrated to material_ui and the next day noticed that a contact avatar no longer flew between the list and the detail page on web. Nothing about the symptom points at routing — it reads as a broken Hero tag, a mismatched tag string, or a Navigator problem. It took a walk through builder.dart to find that the page had become a NoTransitionPage. Anyone with shared-element transitions will lose them here, and I suspect most will not connect it to isMaterialApp.
Minimal check, same shape as your NoTransitionPage test — a hero flight lives in the overlay, so more than one Hero exists mid-transition:
Widget hero(double size) => Hero(tag: 'pic', child: SizedBox.square(dimension: size));
// route '/detail' declared with builder: -> find.byType(Hero) has 1 match (no flight)
// route '/detail' declared with pageBuilder: -> find.byType(Hero) has 2 matches (flight)
router.go('/detail');
await tester.pump();
await tester.pump(const Duration(milliseconds: 100));
expect(tester.widgetList(find.byType(Hero)).length > 1, isTrue);
Two more things worth folding into this issue:
The part that makes this expensive is how quiet it is: no analyzer diagnostic, no assertion, no exception — just log('Using WidgetsApp configuration'), which is invisible unless debugLogDiagnostics: true happens to be set. Two first-party packages disagree about a type and the app degrades in silence.
mbcorona commentedon Aug 15, 2026
Thanks for the detailed report, routing to the framework team for evaluation.
dkwingsmt commentedon Aug 19, 2026
cc @chunhtai
chunhtai commentedon Aug 19, 2026
this pr should address this flutter/packages#12504
[go_router] migrates to material_ui and cupertino_ui (flutter#12504)
github-actions commentedon Sep 2, 2026
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of
flutter doctor -vand a minimal reproduction of the issue.