Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 25 additions & 12 deletions packages/go_router/lib/src/builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'logging.dart';
import 'match.dart';
import 'misc/error_screen.dart';
import 'misc/errors.dart';
import 'pages/app_type.dart';
import 'pages/cupertino.dart';
import 'pages/custom_transition_page.dart';
import 'pages/material.dart';
Expand Down Expand Up @@ -180,17 +181,18 @@ class _CustomNavigatorState extends State<_CustomNavigator> {
void didChangeDependencies() {
super.didChangeDependencies();
// Create a HeroController based on the app type.
if (_controller == null) {
if (isMaterialApp(context)) {
_controller = createMaterialHeroController();
} else if (isCupertinoApp(context)) {
_controller = createCupertinoHeroController();
} else {
_controller = HeroController();
}
}
_controller ??= switch (appTypeOf(context)) {
AppType.sdkMaterial => createSdkMaterialHeroController(),
AppType.materialUi => createMaterialHeroController(),
AppType.sdkCupertino => createSdkCupertinoHeroController(),
AppType.cupertinoUi => createCupertinoHeroController(),
null => HeroController(),
};
// This method can also be called if any of the page builders depend on
// the context. In this case, make sure _pages are rebuilt.
// the context. In this case, make sure the app-type builders and pages are
// recomputed from the current ancestor tree.
_pageBuilderForAppType = null;
_errorBuilderForAppType = null;
_pages = null;
Comment thread
yazanmg marked this conversation as resolved.
}

Expand Down Expand Up @@ -347,12 +349,23 @@ class _CustomNavigatorState extends State<_CustomNavigator> {

// can be null during testing
final Element? elem = context is Element ? context : null;
final AppType? appType = elem == null ? null : appTypeOf(elem);

if (elem != null && isMaterialApp(elem)) {
if (appType == AppType.sdkMaterial) {
log('Using MaterialApp configuration');
_pageBuilderForAppType = pageBuilderForSdkMaterialApp;
_errorBuilderForAppType = (BuildContext c, GoRouterState s) =>
SdkMaterialErrorScreen(s.error);
} else if (appType == AppType.materialUi) {
log('Using MaterialApp configuration');
_pageBuilderForAppType = pageBuilderForMaterialApp;
_errorBuilderForAppType = (BuildContext c, GoRouterState s) => MaterialErrorScreen(s.error);
} else if (elem != null && isCupertinoApp(elem)) {
} else if (appType == AppType.sdkCupertino) {
log('Using CupertinoApp configuration');
_pageBuilderForAppType = pageBuilderForSdkCupertinoApp;
_errorBuilderForAppType = (BuildContext c, GoRouterState s) =>
SdkCupertinoErrorScreen(s.error);
} else if (appType == AppType.cupertinoUi) {
log('Using CupertinoApp configuration');
_pageBuilderForAppType = pageBuilderForCupertinoApp;
_errorBuilderForAppType = (BuildContext c, GoRouterState s) =>
Expand Down
54 changes: 54 additions & 0 deletions packages/go_router/lib/src/pages/app_type.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:cupertino_ui/cupertino_ui.dart' as cupertino_ui;
import 'package:flutter/cupertino.dart' as flutter_cupertino;
import 'package:flutter/material.dart' as flutter_material;
import 'package:flutter/widgets.dart';
import 'package:material_ui/material_ui.dart' as material_ui;

/// The app implementation that contains a go_router Navigator.
enum AppType {
/// A MaterialApp from the Flutter SDK.
sdkMaterial,

/// A MaterialApp from material_ui.
materialUi,

/// A CupertinoApp from the Flutter SDK.
sdkCupertino,

/// A CupertinoApp from cupertino_ui.
cupertinoUi,
}

/// Finds the supported app implementation to use for the Navigator.
///
/// Material apps retain precedence over Cupertino apps, matching the existing
/// go_router adapter selection behavior. Within each app family, the closest
/// supported implementation is used.
AppType? appTypeOf(BuildContext context) {
AppType? nearestCupertino;
AppType? material;
context.visitAncestorElements((Element element) {
final Widget widget = element.widget;
if (widget is flutter_material.MaterialApp) {
material = AppType.sdkMaterial;
return false;
}
if (widget is material_ui.MaterialApp) {
material = AppType.materialUi;
return false;
}
if (nearestCupertino == null) {
if (widget is flutter_cupertino.CupertinoApp) {
nearestCupertino = AppType.sdkCupertino;
} else if (widget is cupertino_ui.CupertinoApp) {
nearestCupertino = AppType.cupertinoUi;
}
}
return true;
});
return material ?? nearestCupertino;
}
70 changes: 60 additions & 10 deletions packages/go_router/lib/src/pages/cupertino.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,48 @@

// ignore_for_file: diagnostic_describe_all_properties

import 'package:cupertino_ui/cupertino_ui.dart';
import 'package:cupertino_ui/cupertino_ui.dart' as cupertino_ui;
import 'package:flutter/cupertino.dart' as flutter_cupertino;
import 'package:flutter/widgets.dart';

import '../misc/extensions.dart';

/// Checks for CupertinoApp in the widget tree.
bool isCupertinoApp(BuildContext context) =>
context.findAncestorWidgetOfExactType<CupertinoApp>() != null;
context.findAncestorWidgetOfExactType<flutter_cupertino.CupertinoApp>() != null ||
context.findAncestorWidgetOfExactType<cupertino_ui.CupertinoApp>() != null;

/// Creates a Cupertino HeroController from cupertino_ui.
HeroController createCupertinoHeroController() =>
cupertino_ui.CupertinoApp.createCupertinoHeroController();

/// Creates a Cupertino HeroController.
HeroController createCupertinoHeroController() => CupertinoApp.createCupertinoHeroController();
/// Creates a Cupertino HeroController from the Flutter SDK.
HeroController createSdkCupertinoHeroController() =>
flutter_cupertino.CupertinoApp.createCupertinoHeroController();

/// Builds a Cupertino page from cupertino_ui.
cupertino_ui.CupertinoPage<void> pageBuilderForCupertinoApp({
required LocalKey key,
required String? name,
required Object? arguments,
required String restorationId,
required Widget child,
}) => cupertino_ui.CupertinoPage<void>(
name: name,
arguments: arguments,
key: key,
restorationId: restorationId,
child: child,
);

/// Builds a Cupertino page.
CupertinoPage<void> pageBuilderForCupertinoApp({
/// Builds a Cupertino page from the Flutter SDK.
flutter_cupertino.CupertinoPage<void> pageBuilderForSdkCupertinoApp({
required LocalKey key,
required String? name,
required Object? arguments,
required String restorationId,
required Widget child,
}) => CupertinoPage<void>(
}) => flutter_cupertino.CupertinoPage<void>(
name: name,
arguments: arguments,
key: key,
Expand All @@ -38,14 +62,40 @@ class CupertinoErrorScreen extends StatelessWidget {
final Exception? error;

@override
Widget build(BuildContext context) => CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(middle: Text('Page Not Found')),
Widget build(BuildContext context) => cupertino_ui.CupertinoPageScaffold(
navigationBar: const cupertino_ui.CupertinoNavigationBar(middle: Text('Page Not Found')),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(error?.toString() ?? 'page not found'),
cupertino_ui.CupertinoButton(onPressed: () => context.go('/'), child: const Text('Home')),
],
),
),
);
}

/// Default error page implementation for an SDK CupertinoApp.
class SdkCupertinoErrorScreen extends StatelessWidget {
/// Provide an exception to this page for it to be displayed.
const SdkCupertinoErrorScreen(this.error, {super.key});

/// The exception to be displayed.
final Exception? error;

@override
Widget build(BuildContext context) => flutter_cupertino.CupertinoPageScaffold(
navigationBar: const flutter_cupertino.CupertinoNavigationBar(middle: Text('Page Not Found')),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(error?.toString() ?? 'page not found'),
CupertinoButton(onPressed: () => context.go('/'), child: const Text('Home')),
flutter_cupertino.CupertinoButton(
onPressed: () => context.go('/'),
child: const Text('Home'),
),
],
),
),
Expand Down
68 changes: 57 additions & 11 deletions packages/go_router/lib/src/pages/material.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,48 @@

// ignore_for_file: diagnostic_describe_all_properties

import 'package:material_ui/material_ui.dart';
import 'package:flutter/material.dart' as flutter_material;
import 'package:flutter/widgets.dart';
import 'package:material_ui/material_ui.dart' as material_ui;

import '../misc/extensions.dart';

/// Checks for MaterialApp in the widget tree.
bool isMaterialApp(BuildContext context) =>
context.findAncestorWidgetOfExactType<MaterialApp>() != null;
context.findAncestorWidgetOfExactType<flutter_material.MaterialApp>() != null ||
context.findAncestorWidgetOfExactType<material_ui.MaterialApp>() != null;

/// Creates a Material HeroController.
HeroController createMaterialHeroController() => MaterialApp.createMaterialHeroController();
/// Creates a Material HeroController from material_ui.
HeroController createMaterialHeroController() =>
material_ui.MaterialApp.createMaterialHeroController();

/// Builds a Material page.
MaterialPage<void> pageBuilderForMaterialApp({
/// Creates a Material HeroController from the Flutter SDK.
HeroController createSdkMaterialHeroController() =>
flutter_material.MaterialApp.createMaterialHeroController();

/// Builds a Material page from material_ui.
material_ui.MaterialPage<void> pageBuilderForMaterialApp({
required LocalKey key,
required String? name,
required Object? arguments,
required String restorationId,
required Widget child,
}) => MaterialPage<void>(
}) => material_ui.MaterialPage<void>(
name: name,
arguments: arguments,
key: key,
restorationId: restorationId,
child: child,
);

/// Builds a Material page from the Flutter SDK.
flutter_material.MaterialPage<void> pageBuilderForSdkMaterialApp({
required LocalKey key,
required String? name,
required Object? arguments,
required String restorationId,
required Widget child,
}) => flutter_material.MaterialPage<void>(
name: name,
arguments: arguments,
key: key,
Expand All @@ -39,14 +62,37 @@ class MaterialErrorScreen extends StatelessWidget {
final Exception? error;

@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(title: const Text('Page Not Found')),
Widget build(BuildContext context) => material_ui.Scaffold(
appBar: material_ui.AppBar(title: const Text('Page Not Found')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
material_ui.SelectableText(error?.toString() ?? 'page not found'),
material_ui.TextButton(onPressed: () => context.go('/'), child: const Text('Home')),
],
),
),
);
}

/// Default error page implementation for an SDK MaterialApp.
class SdkMaterialErrorScreen extends StatelessWidget {
/// Provide an exception to this page for it to be displayed.
const SdkMaterialErrorScreen(this.error, {super.key});

/// The exception to be displayed.
final Exception? error;

@override
Widget build(BuildContext context) => flutter_material.Scaffold(
appBar: flutter_material.AppBar(title: const Text('Page Not Found')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SelectableText(error?.toString() ?? 'page not found'),
TextButton(onPressed: () => context.go('/'), child: const Text('Home')),
flutter_material.SelectableText(error?.toString() ?? 'page not found'),
flutter_material.TextButton(onPressed: () => context.go('/'), child: const Text('Home')),
],
),
),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
changelog: |
- Restores SDK MaterialApp and CupertinoApp support when selecting default pages, hero controllers, and error screens, while preserving material_ui and cupertino_ui support.
version: patch
Loading