Commit 9693cd55 authored by Jason Simmons's avatar Jason Simmons

Add a LocaleQuery widget that can be used to fetch locale-specific data

Users of MaterialApp can provide an onLocaleChanged handler that will be
called to asynchronously fetch locale-specific data.  MaterialApp will
then instantiate a LocaleQuery that supplies the locale data to its
descendants.
parent 1e1761d1
...@@ -94,6 +94,14 @@ class StocksAppState extends State<StocksApp> { ...@@ -94,6 +94,14 @@ class StocksAppState extends State<StocksApp> {
return null; return null;
} }
Future<LocaleQueryData> _onLocaleChanged(ui.Locale locale) {
String localeString = locale.toString();
return initializeMessages(localeString).then((_) {
Intl.defaultLocale = localeString;
return StockStrings.instance;
});
}
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new MaterialApp( return new MaterialApp(
title: 'Stocks', title: 'Stocks',
...@@ -102,13 +110,12 @@ class StocksAppState extends State<StocksApp> { ...@@ -102,13 +110,12 @@ class StocksAppState extends State<StocksApp> {
'/': (RouteArguments args) => new StockHome(_stocks, _symbols, _optimismSetting, modeUpdater), '/': (RouteArguments args) => new StockHome(_stocks, _symbols, _optimismSetting, modeUpdater),
'/settings': (RouteArguments args) => new StockSettings(_optimismSetting, _backupSetting, settingsUpdater) '/settings': (RouteArguments args) => new StockSettings(_optimismSetting, _backupSetting, settingsUpdater)
}, },
onGenerateRoute: _getRoute onGenerateRoute: _getRoute,
onLocaleChanged: _onLocaleChanged
); );
} }
} }
void main() { void main() {
initializeMessages(Intl.defaultLocale).then((_) {
runApp(new StocksApp()); runApp(new StocksApp());
});
} }
...@@ -147,7 +147,7 @@ class StockHomeState extends State<StockHome> { ...@@ -147,7 +147,7 @@ class StockHomeState extends State<StockHome> {
Widget buildToolBar() { Widget buildToolBar() {
return new ToolBar( return new ToolBar(
elevation: 0, elevation: 0,
center: new Text(StockStrings.title()), center: new Text(StockStrings.of(context).title()),
right: <Widget>[ right: <Widget>[
new IconButton( new IconButton(
icon: "action/search", icon: "action/search",
...@@ -161,8 +161,9 @@ class StockHomeState extends State<StockHome> { ...@@ -161,8 +161,9 @@ class StockHomeState extends State<StockHome> {
tabBar: new TabBar( tabBar: new TabBar(
selection: _tabBarSelection, selection: _tabBarSelection,
labels: <TabLabel>[ labels: <TabLabel>[
new TabLabel(text: StockStrings.market()), new TabLabel(text: StockStrings.of(context).market()),
new TabLabel(text: StockStrings.portfolio())] new TabLabel(text: StockStrings.of(context).portfolio())
]
) )
); );
} }
......
...@@ -8,20 +8,26 @@ part of stocks; ...@@ -8,20 +8,26 @@ part of stocks;
// To generate the stock_messages_*.dart files from the ARB files, run: // To generate the stock_messages_*.dart files from the ARB files, run:
// pub run intl:generate_from_arb --output-dir=lib/i18n --generated-file-prefix=stock_ --no-use-deferred-loading lib/stock_strings.dart lib/i18n/stocks_*.arb // pub run intl:generate_from_arb --output-dir=lib/i18n --generated-file-prefix=stock_ --no-use-deferred-loading lib/stock_strings.dart lib/i18n/stocks_*.arb
class StockStrings { class StockStrings extends LocaleQueryData {
static String title() => Intl.message( static StockStrings of(BuildContext context) {
return LocaleQuery.of(context);
}
static final StockStrings instance = new StockStrings();
String title() => Intl.message(
'Stocks', 'Stocks',
name: 'title', name: 'title',
desc: 'Title for the Stocks application' desc: 'Title for the Stocks application'
); );
static String market() => Intl.message( String market() => Intl.message(
'MARKET', 'MARKET',
name: 'market', name: 'market',
desc: 'Label for the Market tab' desc: 'Label for the Market tab'
); );
static String portfolio() => Intl.message( String portfolio() => Intl.message(
'PORTFOLIO', 'PORTFOLIO',
name: 'portfolio', name: 'portfolio',
desc: 'Label for the Portfolio tab' desc: 'Label for the Portfolio tab'
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
...@@ -37,13 +38,16 @@ class RouteArguments { ...@@ -37,13 +38,16 @@ class RouteArguments {
} }
typedef Widget RouteBuilder(RouteArguments args); typedef Widget RouteBuilder(RouteArguments args);
typedef Future<LocaleQueryData> LocaleChangedCallback(ui.Locale locale);
class MaterialApp extends StatefulComponent { class MaterialApp extends StatefulComponent {
MaterialApp({ MaterialApp({
Key key, Key key,
this.title, this.title,
this.theme, this.theme,
this.routes: const <String, RouteBuilder>{}, this.routes: const <String, RouteBuilder>{},
this.onGenerateRoute this.onGenerateRoute,
this.onLocaleChanged
}) : super(key: key) { }) : super(key: key) {
assert(routes != null); assert(routes != null);
assert(routes.containsKey(Navigator.defaultRouteName) || onGenerateRoute != null); assert(routes.containsKey(Navigator.defaultRouteName) || onGenerateRoute != null);
...@@ -53,6 +57,7 @@ class MaterialApp extends StatefulComponent { ...@@ -53,6 +57,7 @@ class MaterialApp extends StatefulComponent {
final ThemeData theme; final ThemeData theme;
final Map<String, RouteBuilder> routes; final Map<String, RouteBuilder> routes;
final RouteFactory onGenerateRoute; final RouteFactory onGenerateRoute;
final LocaleChangedCallback onLocaleChanged;
_MaterialAppState createState() => new _MaterialAppState(); _MaterialAppState createState() => new _MaterialAppState();
} }
...@@ -62,11 +67,13 @@ class _MaterialAppState extends State<MaterialApp> implements BindingObserver { ...@@ -62,11 +67,13 @@ class _MaterialAppState extends State<MaterialApp> implements BindingObserver {
GlobalObjectKey _navigator; GlobalObjectKey _navigator;
Size _size; Size _size;
LocaleQueryData _localeData;
void initState() { void initState() {
super.initState(); super.initState();
_navigator = new GlobalObjectKey(this); _navigator = new GlobalObjectKey(this);
_size = ui.window.size; _size = ui.window.size;
didChangeLocale(ui.window.locale);
FlutterBinding.instance.addObserver(this); FlutterBinding.instance.addObserver(this);
} }
...@@ -88,6 +95,15 @@ class _MaterialAppState extends State<MaterialApp> implements BindingObserver { ...@@ -88,6 +95,15 @@ class _MaterialAppState extends State<MaterialApp> implements BindingObserver {
void didChangeSize(Size size) => setState(() { _size = size; }); void didChangeSize(Size size) => setState(() { _size = size; });
void didChangeLocale(ui.Locale locale) {
if (config.onLocaleChanged != null) {
config.onLocaleChanged(locale).then((LocaleQueryData data) {
if (mounted)
setState(() { _localeData = data; });
});
}
}
final HeroController _heroController = new HeroController(); final HeroController _heroController = new HeroController();
Route _generateRoute(NamedRouteSettings settings) { Route _generateRoute(NamedRouteSettings settings) {
...@@ -106,9 +122,17 @@ class _MaterialAppState extends State<MaterialApp> implements BindingObserver { ...@@ -106,9 +122,17 @@ class _MaterialAppState extends State<MaterialApp> implements BindingObserver {
} }
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (config.onLocaleChanged != null && _localeData == null) {
// If the app expects a locale but we don't yet know the locale, then
// don't build the widgets now.
return new Container();
}
ThemeData theme = config.theme ?? new ThemeData.fallback(); ThemeData theme = config.theme ?? new ThemeData.fallback();
return new MediaQuery( return new MediaQuery(
data: new MediaQueryData(size: _size), data: new MediaQueryData(size: _size),
child: new LocaleQuery(
data: _localeData,
child: new Theme( child: new Theme(
data: theme, data: theme,
child: new DefaultTextStyle( child: new DefaultTextStyle(
...@@ -128,6 +152,7 @@ class _MaterialAppState extends State<MaterialApp> implements BindingObserver { ...@@ -128,6 +152,7 @@ class _MaterialAppState extends State<MaterialApp> implements BindingObserver {
) )
) )
) )
)
); );
} }
......
...@@ -197,6 +197,7 @@ class _PointerEventConverter { ...@@ -197,6 +197,7 @@ class _PointerEventConverter {
class BindingObserver { class BindingObserver {
bool didPopRoute() => false; bool didPopRoute() => false;
void didChangeSize(Size size) { } void didChangeSize(Size size) { }
void didChangeLocale(ui.Locale locale) { }
} }
/// The glue between the render tree and the Flutter engine /// The glue between the render tree and the Flutter engine
...@@ -208,6 +209,7 @@ class FlutterBinding extends HitTestTarget { ...@@ -208,6 +209,7 @@ class FlutterBinding extends HitTestTarget {
ui.window.onPointerPacket = _handlePointerPacket; ui.window.onPointerPacket = _handlePointerPacket;
ui.window.onMetricsChanged = _handleMetricsChanged; ui.window.onMetricsChanged = _handleMetricsChanged;
ui.window.onLocaleChanged = _handleLocaleChanged;
ui.window.onPopRoute = _handlePopRoute; ui.window.onPopRoute = _handlePopRoute;
if (renderViewOverride == null) { if (renderViewOverride == null) {
...@@ -244,6 +246,11 @@ class FlutterBinding extends HitTestTarget { ...@@ -244,6 +246,11 @@ class FlutterBinding extends HitTestTarget {
observer.didChangeSize(size); observer.didChangeSize(size);
} }
void _handleLocaleChanged() {
for (BindingObserver observer in _observers)
observer.didChangeLocale(ui.window.locale);
}
void _handlePersistentFrameCallback(Duration timeStamp) { void _handlePersistentFrameCallback(Duration timeStamp) {
beginFrame(); beginFrame();
} }
......
// Copyright 2015 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 'framework.dart';
// Superclass for locale-specific data provided by the application.
class LocaleQueryData { }
class LocaleQuery<T extends LocaleQueryData> extends InheritedWidget {
LocaleQuery({
Key key,
this.data,
Widget child
}) : super(key: key, child: child) {
assert(child != null);
}
final T data;
static LocaleQueryData of(BuildContext context) {
LocaleQuery query = context.inheritFromWidgetOfType(LocaleQuery);
return query == null ? null : query.data;
}
bool updateShouldNotify(LocaleQuery old) => data != old.data;
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
description.add('$data');
}
}
...@@ -19,6 +19,7 @@ export 'src/widgets/gesture_detector.dart'; ...@@ -19,6 +19,7 @@ export 'src/widgets/gesture_detector.dart';
export 'src/widgets/gridpaper.dart'; export 'src/widgets/gridpaper.dart';
export 'src/widgets/heroes.dart'; export 'src/widgets/heroes.dart';
export 'src/widgets/homogeneous_viewport.dart'; export 'src/widgets/homogeneous_viewport.dart';
export 'src/widgets/locale_query.dart';
export 'src/widgets/media_query.dart'; export 'src/widgets/media_query.dart';
export 'src/widgets/mimic.dart'; export 'src/widgets/mimic.dart';
export 'src/widgets/mixed_viewport.dart'; export 'src/widgets/mixed_viewport.dart';
......
...@@ -7,8 +7,8 @@ dependencies: ...@@ -7,8 +7,8 @@ dependencies:
collection: '>=1.1.3 <2.0.0' collection: '>=1.1.3 <2.0.0'
intl: '>=0.12.4+2 <0.13.0' intl: '>=0.12.4+2 <0.13.0'
material_design_icons: '>=0.0.3 <0.1.0' material_design_icons: '>=0.0.3 <0.1.0'
sky_engine: 0.0.67 sky_engine: 0.0.68
sky_services: 0.0.67 sky_services: 0.0.68
vector_math: '>=1.4.3 <2.0.0' vector_math: '>=1.4.3 <2.0.0'
# To pin the transitive dependency through mojo_sdk. # To pin the transitive dependency through mojo_sdk.
......
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