From af834eed232e3aa2b24024a4335ff44b54cae194 Mon Sep 17 00:00:00 2001 From: Juanjo Tugores Date: Fri, 2 Aug 2024 13:00:43 -0700 Subject: [PATCH] Make the App's title optional on web (#152003) Title (in web) results in updating the [title element][1] which is a global property. This is problematic in embedded and multiview modes as title should be managed by host apps. This PR makes the title optional, hence if not provided it won't result in the website title being updated. --- packages/flutter/lib/src/cupertino/app.dart | 6 +++--- packages/flutter/lib/src/material/app.dart | 4 ++-- packages/flutter/lib/src/widgets/app.dart | 17 +++++++++++------ packages/flutter/test/cupertino/app_test.dart | 2 ++ packages/flutter/test/widgets/app_test.dart | 11 +++++++++++ 5 files changed, 29 insertions(+), 11 deletions(-) diff --git a/packages/flutter/lib/src/cupertino/app.dart b/packages/flutter/lib/src/cupertino/app.dart index 729f2d3ee1..cfb848dc76 100644 --- a/packages/flutter/lib/src/cupertino/app.dart +++ b/packages/flutter/lib/src/cupertino/app.dart @@ -166,7 +166,7 @@ class CupertinoApp extends StatefulWidget { this.onNavigationNotification, List this.navigatorObservers = const [], this.builder, - this.title = '', + this.title, this.onGenerateTitle, this.color, this.locale, @@ -207,7 +207,7 @@ class CupertinoApp extends StatefulWidget { this.routerConfig, this.theme, this.builder, - this.title = '', + this.title, this.onGenerateTitle, this.onNavigationNotification, this.color, @@ -303,7 +303,7 @@ class CupertinoApp extends StatefulWidget { /// {@macro flutter.widgets.widgetsApp.title} /// /// This value is passed unmodified to [WidgetsApp.title]. - final String title; + final String? title; /// {@macro flutter.widgets.widgetsApp.onGenerateTitle} /// diff --git a/packages/flutter/lib/src/material/app.dart b/packages/flutter/lib/src/material/app.dart index 582c09166a..bedaf4ad02 100644 --- a/packages/flutter/lib/src/material/app.dart +++ b/packages/flutter/lib/src/material/app.dart @@ -275,7 +275,7 @@ class MaterialApp extends StatefulWidget { this.routerConfig, this.backButtonDispatcher, this.builder, - this.title = '', + this.title, this.onGenerateTitle, this.onNavigationNotification, this.color, @@ -386,7 +386,7 @@ class MaterialApp extends StatefulWidget { /// {@macro flutter.widgets.widgetsApp.title} /// /// This value is passed unmodified to [WidgetsApp.title]. - final String title; + final String? title; /// {@macro flutter.widgets.widgetsApp.onGenerateTitle} /// diff --git a/packages/flutter/lib/src/widgets/app.dart b/packages/flutter/lib/src/widgets/app.dart index b19842e50d..f2bd6273cf 100644 --- a/packages/flutter/lib/src/widgets/app.dart +++ b/packages/flutter/lib/src/widgets/app.dart @@ -328,7 +328,7 @@ class WidgetsApp extends StatefulWidget { this.home, Map this.routes = const {}, this.builder, - this.title = '', + this.title, this.onGenerateTitle, this.textStyle, required this.color, @@ -425,7 +425,7 @@ class WidgetsApp extends StatefulWidget { this.routerConfig, this.backButtonDispatcher, this.builder, - this.title = '', + this.title, this.onGenerateTitle, this.onNavigationNotification, this.textStyle, @@ -828,7 +828,7 @@ class WidgetsApp extends StatefulWidget { /// /// To provide a localized title instead, use [onGenerateTitle]. /// {@endtemplate} - final String title; + final String? title; /// {@template flutter.widgets.widgetsApp.onGenerateTitle} /// If non-null this callback function is called to produce the app's @@ -1771,7 +1771,7 @@ class _WidgetsAppState extends State with WidgetsBindingObserver { return true; }()); - final Widget title; + final Widget? title; if (widget.onGenerateTitle != null) { title = Builder( // This Builder exists to provide a context below the Localizations widget. @@ -1786,9 +1786,14 @@ class _WidgetsAppState extends State with WidgetsBindingObserver { ); }, ); + } else if (widget.title == null && kIsWeb) { + // Updating the element in the DOM is problematic in embedded + // and multiview modes as title should be managed by host apps. + // Refer to https://github.com/flutter/flutter/pull/152003 for more info. + title = null; } else { title = Title( - title: widget.title, + title: widget.title ?? '', color: widget.color.withOpacity(1.0), child: result, ); @@ -1823,7 +1828,7 @@ class _WidgetsAppState extends State<WidgetsApp> with WidgetsBindingObserver { child: Localizations( locale: appLocale, delegates: _localizationsDelegates.toList(), - child: title, + child: title ?? result, ), ), ), diff --git a/packages/flutter/test/cupertino/app_test.dart b/packages/flutter/test/cupertino/app_test.dart index 787a05113a..8c6f9e7ec5 100644 --- a/packages/flutter/test/cupertino/app_test.dart +++ b/packages/flutter/test/cupertino/app_test.dart @@ -70,6 +70,7 @@ void main() { ); await tester.pumpWidget(const CupertinoApp( theme: CupertinoThemeData(brightness: Brightness.light), + title: '', color: dynamicColor, home: Placeholder(), )); @@ -79,6 +80,7 @@ void main() { await tester.pumpWidget(const CupertinoApp( theme: CupertinoThemeData(brightness: Brightness.dark), color: dynamicColor, + title: '', home: Placeholder(), )); diff --git a/packages/flutter/test/widgets/app_test.dart b/packages/flutter/test/widgets/app_test.dart index 46070633c4..966fb3c125 100644 --- a/packages/flutter/test/widgets/app_test.dart +++ b/packages/flutter/test/widgets/app_test.dart @@ -153,6 +153,17 @@ void main() { expect(checked, isTrue); }, variant: KeySimulatorTransitModeVariant.all()); + testWidgets('Title is not created if title is not passed and kIsweb', (WidgetTester tester) async { + await tester.pumpWidget( + WidgetsApp( + color: const Color(0xFF123456), + builder: (BuildContext context, Widget? child) => Container(), + ), + ); + + expect(find.byType(Title), kIsWeb ? findsNothing : findsOneWidget); + }); + group('error control test', () { Future<void> expectFlutterError({ required GlobalKey<NavigatorState> key,