Web apps usually reflect the currently displayed content in the tab's title.
Supplying the title
parameter to a MaterialApp
or WidgetsApp
means that I cannot change it without rebuilding the entire tree, i.e. calling setState
at the top level where I return my MaterialApp
.
MaterialApp(
title: 'Web app title',
...,
)
I want to be able to change the app title from anywhere in the app at any time. How do I do that?
It's almost 2 years late, but I found my decent solution for this. It's not that simple but pretty usable.
main.dart
inside MaterialApp
, add navigatorObservers
:
MaterialApp(
initialRoute: "/welcome",
routes: <String, WidgetBuilder>{
'/welcome': (BuildContext context) => WelcomeScreen(),
'/login': (BuildContext context) => LoginScreen(),
'/register': (BuildContext context) => RegisterScreen(),
},
// other codes ...
navigatorObservers: [MyRouteObserver()],
// other codes ...
);
and at the very bottom, add:
class MyRouteObserver extends RouteObserver<PageRoute<dynamic>> {
void _setTitle(String what, PageRoute<dynamic> routeFrom, PageRoute<dynamic> routeTo) {
final oldScreenName = routeFrom.settings.name;
final newScreenName = routeTo.settings.name;
log("route changed: $what ($oldScreenName -> $newScreenName)"); // it's just route activity log, don't forget to import 'dart:developer';
final uri = Uri.parse(newScreenName ?? "");
final routeName = uri.pathSegments.isNotEmpty ? uri.pathSegments.first : "";
switch ("/$routeName") {
case "/welcome" : return setTitle("Welcome");
case "/login" : return setTitle("Login");
case "/register" : return setTitle("Sign Up");
// etc ...
}
setTitle("Not Found");
}
@override
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
super.didPush(route, previousRoute);
if (route is PageRoute && previousRoute is PageRoute) _setTitle("push", previousRoute, route);
}
@override
void didReplace({Route<dynamic>? newRoute, Route<dynamic>? oldRoute}) {
super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
if (newRoute is PageRoute && oldRoute is PageRoute) _setTitle("replace", oldRoute, newRoute);
}
@override
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
super.didPop(route, previousRoute);
if (route is PageRoute && previousRoute is PageRoute) _setTitle("pop", route, previousRoute);
}
}
helpers.dart
void setTitle([String? title]) async {
Future.microtask(() {
SystemChrome.setApplicationSwitcherDescription(ApplicationSwitcherDescription(
label: "Your APP | ${title ?? 'Your Tagline'}",
primaryColor: Colors.blue.value, // your app primary color
));
});
}
finally, if you want to override title individually in specific page, do:
@override
void initState() {
super.initState();
WidgetsBinding.instance?.addPostFrameCallback((_) {
setTitle("Custom Page Title");
});
}
The app title can be dynamically changed on all platforms using SystemChrome.setApplicationSwitcherDescription
:
@override
Widget build(BuildContext context) {
SystemChrome.setApplicationSwitcherDescription(ApplicationSwitcherDescription(
label: 'Dynamic web app title',
primaryColor: Theme.of(context).primaryColor.value,
));
return Container(...);
}
SystemChrome
becomes available with import 'package:flutter/services.dart';
and you want to call setApplicationSwitcherDescription
in your build
method.
This is also how Title
does it (see source code), which is the widget used by WidgetsApp
, MaterialApp
, etc.
Thus, you can also use a Title
widget instead of accessing SystemChrome
yourself:
@override
Widget build(Context context) {
return Title(
label: 'Dynamic app title',
primaryColor: Theme.of(context).primaryColor,
child: ..,
);
}
label
The label
parameter is pretty straight forward: it maps exactly to what title
is in your MaterialApp
and just a String
.
primaryColor
The primaryColor
will determine the color the system might use in the application switcher, e.g. the title bar color in recents on Android.
This is an int
instead of a Color
, which is why you need to call Color.value
to convert your primary color to an integer.
Title
sYou might ask yourself if calling setApplicationSwitcherDescription
or inserting a Title
widget when the title
property on MaterialApp
is set can cause any problems.
The answer is: no because the widget that is the deepest down in the tree will set the title. Essentially, even if the whole app rebuilds, your setApplicationSwitcherDescription
will be called after that of MaterialApp
assuming you are calling it in a child and therefore you will override the MaterialApp
title.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With