Commit e57d94c3 authored by Hans Muller's avatar Hans Muller Committed by GitHub

Make it possible to localize an app title (#12105)

parent 9d59fb0c
......@@ -85,6 +85,7 @@ class MaterialApp extends StatefulWidget {
MaterialApp({ // can't be const because the asserts use methods on Map :-(
Key key,
this.title: '',
this.onGenerateTitle,
this.color,
this.theme,
this.home,
......@@ -132,9 +133,30 @@ class MaterialApp extends StatefulWidget {
),
super(key: key);
/// A one-line description of this app for use in the window manager.
/// A one-line description used by the device to identify the app for the user.
///
/// On Android the titles appear above the task manager's app snapshots which are
/// displayed when the user presses the "recent apps" button. Similarly, on
/// iOS the titles appear in the App Switcher when the user double presses the
/// home button.
///
/// To provide a localized title instead, use [onGenerateTitle].
///
/// This value is passed unmodified to [WidgetsApp.title].
final String title;
/// If non-null this function is called to produce the app's
/// title string, otherwise [title] is used.
///
/// The [onGenerateTitle] `context` parameter includes the [WidgetApp]'s
/// [Localizations] widget so that this callback can be used to produce a
/// localized title.
///
/// This callback function must not return null.
///
/// This value is passed unmodified to [WidgetsApp.onGenerateTitle].
final GenerateAppTitle onGenerateTitle;
/// The colors to use for the application's widgets.
final ThemeData theme;
......@@ -320,7 +342,7 @@ class MaterialApp extends StatefulWidget {
/// configure this list to match the locales they support.
///
/// This list must not null. It's default value is just
/// `[const Locale('en', 'US')]`. It is simply passed along to the
/// `[const Locale('en', 'US')]`. It is passed along unmodified to the
/// [WidgetsApp] built by this widget.
///
/// The order of the list matters. By default, if the device's locale doesn't
......@@ -508,6 +530,7 @@ class _MaterialAppState extends State<MaterialApp> {
child: new WidgetsApp(
key: new GlobalObjectKey(this),
title: widget.title,
onGenerateTitle: widget.onGenerateTitle,
textStyle: _errorTextStyle,
// blue is the primary color of the default theme
color: widget.color ?? theme?.primaryColor ?? Colors.blue,
......
......@@ -34,6 +34,16 @@ export 'dart:ui' show Locale;
/// parameter is just the value of [WidgetApp.supportedLocales].
typedef Locale LocaleResolutionCallback(Locale locale, Iterable<Locale> supportedLocales);
/// The signature of [WidgetsApp.onGenerateTitle].
///
/// Used to generate a value for the app's [Title.title], which the device uses
/// to identify the app for the user. The `context` includes the [WidgetApp]'s
/// [Localizations] widget so that this method can be used to produce a
/// localized title.
///
/// This function must not return null.
typedef String GenerateAppTitle(BuildContext context);
// Delegate that fetches the default (English) strings.
class _WidgetsLocalizationsDelegate extends LocalizationsDelegate<WidgetsLocalizations> {
const _WidgetsLocalizationsDelegate();
......@@ -71,6 +81,7 @@ class WidgetsApp extends StatefulWidget {
@required this.onGenerateRoute,
this.onUnknownRoute,
this.title: '',
this.onGenerateTitle,
this.textStyle,
@required this.color,
this.navigatorObservers: const <NavigatorObserver>[],
......@@ -99,9 +110,26 @@ class WidgetsApp extends StatefulWidget {
assert(debugShowWidgetInspector != null),
super(key: key);
/// A one-line description of this app for use in the window manager.
/// A one-line description used by the device to identify the app for the user.
///
/// On Android the titles appear above the task manager's app snapshots which are
/// displayed when the user presses the "recent apps" button. Similarly, on
/// iOS the titles appear in the App Switcher when the user double presses the
/// home button.
///
/// To provide a localized title instead, use [onGenerateTitle].
final String title;
/// If non-null this callback function is called to produce the app's
/// title string, otherwise [title] is used.
///
/// The [onGenerateTitle] `context` parameter includes the [WidgetApp]'s
/// [Localizations] widget so that this callback can be used to produce a
/// localized title.
///
/// This callback function must not return null.
final GenerateAppTitle onGenerateTitle;
/// The default text style for [Text] in the application.
final TextStyle textStyle;
......@@ -465,10 +493,22 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
child: new Localizations(
locale: widget.locale ?? _locale,
delegates: _localizationsDelegates.toList(),
child: new Title(
title: widget.title,
color: widget.color,
child: result,
// This Builder exists to provide a context below the Localizations widget.
// The onGenerateCallback() can refer to Localizations via its context
// parameter.
child: new Builder(
builder: (BuildContext context) {
String title = widget.title;
if (widget.onGenerateTitle != null) {
title = widget.onGenerateTitle(context);
assert(title != null, 'onGenerateTitle must return a non-null String');
}
return new Title(
title: title,
color: widget.color,
child: result,
);
},
),
),
);
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
const Color kTitleColor = const Color(0xFF333333);
const String kTitleString = 'Hello World';
Future<Null> pumpApp(WidgetTester tester, { GenerateAppTitle onGenerateTitle }) async {
await tester.pumpWidget(
new WidgetsApp(
supportedLocales: <Locale>[
const Locale('en', 'US'),
const Locale('en', 'GB'),
],
title: kTitleString,
color: kTitleColor,
onGenerateTitle: onGenerateTitle,
onGenerateRoute: (RouteSettings settings) {
return new PageRouteBuilder<Null>(
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
return new Container();
}
);
},
),
);
}
void main() {
testWidgets('Specified title and color are used to build a Title', (WidgetTester tester) async {
await pumpApp(tester);
expect(tester.widget<Title>(find.byType(Title)).title, kTitleString);
expect(tester.widget<Title>(find.byType(Title)).color, kTitleColor);
});
testWidgets('onGenerateTitle handles changing locales', (WidgetTester tester) async {
String generateTitle(BuildContext context) {
return Localizations.localeOf(context).toString();
}
await pumpApp(tester, onGenerateTitle: generateTitle);
expect(tester.widget<Title>(find.byType(Title)).title, 'en_US');
expect(tester.widget<Title>(find.byType(Title)).color, kTitleColor);
await tester.binding.setLocale('en', 'GB');
await tester.pump();
expect(tester.widget<Title>(find.byType(Title)).title, 'en_GB');
expect(tester.widget<Title>(find.byType(Title)).color, kTitleColor);
// Not a supported locale, so we switch to supportedLocales[0], en_US
await tester.binding.setLocale('fr', 'CA');
await tester.pump();
expect(tester.widget<Title>(find.byType(Title)).title, 'en_US');
expect(tester.widget<Title>(find.byType(Title)).color, kTitleColor);
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment