diff --git a/examples/stocks/lib/main.dart b/examples/stocks/lib/main.dart index 24cde032da8..f21d6fbb44d 100644 --- a/examples/stocks/lib/main.dart +++ b/examples/stocks/lib/main.dart @@ -94,6 +94,14 @@ class StocksAppState extends State { return null; } + Future _onLocaleChanged(ui.Locale locale) { + String localeString = locale.toString(); + return initializeMessages(localeString).then((_) { + Intl.defaultLocale = localeString; + return StockStrings.instance; + }); + } + Widget build(BuildContext context) { return new MaterialApp( title: 'Stocks', @@ -102,13 +110,12 @@ class StocksAppState extends State { '/': (RouteArguments args) => new StockHome(_stocks, _symbols, _optimismSetting, modeUpdater), '/settings': (RouteArguments args) => new StockSettings(_optimismSetting, _backupSetting, settingsUpdater) }, - onGenerateRoute: _getRoute + onGenerateRoute: _getRoute, + onLocaleChanged: _onLocaleChanged ); } } void main() { - initializeMessages(Intl.defaultLocale).then((_) { - runApp(new StocksApp()); - }); + runApp(new StocksApp()); } diff --git a/examples/stocks/lib/stock_home.dart b/examples/stocks/lib/stock_home.dart index 25500e05148..e56d3864984 100644 --- a/examples/stocks/lib/stock_home.dart +++ b/examples/stocks/lib/stock_home.dart @@ -147,7 +147,7 @@ class StockHomeState extends State { Widget buildToolBar() { return new ToolBar( elevation: 0, - center: new Text(StockStrings.title()), + center: new Text(StockStrings.of(context).title()), right: [ new IconButton( icon: "action/search", @@ -161,8 +161,9 @@ class StockHomeState extends State { tabBar: new TabBar( selection: _tabBarSelection, labels: [ - new TabLabel(text: StockStrings.market()), - new TabLabel(text: StockStrings.portfolio())] + new TabLabel(text: StockStrings.of(context).market()), + new TabLabel(text: StockStrings.of(context).portfolio()) + ] ) ); } diff --git a/examples/stocks/lib/stock_strings.dart b/examples/stocks/lib/stock_strings.dart index 77b95350444..e65d1f32732 100644 --- a/examples/stocks/lib/stock_strings.dart +++ b/examples/stocks/lib/stock_strings.dart @@ -8,20 +8,26 @@ part of stocks; // 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 -class StockStrings { - static String title() => Intl.message( +class StockStrings extends LocaleQueryData { + static StockStrings of(BuildContext context) { + return LocaleQuery.of(context); + } + + static final StockStrings instance = new StockStrings(); + + String title() => Intl.message( 'Stocks', name: 'title', desc: 'Title for the Stocks application' ); - static String market() => Intl.message( + String market() => Intl.message( 'MARKET', name: 'market', desc: 'Label for the Market tab' ); - static String portfolio() => Intl.message( + String portfolio() => Intl.message( 'PORTFOLIO', name: 'portfolio', desc: 'Label for the Portfolio tab' diff --git a/packages/flutter/lib/src/material/material_app.dart b/packages/flutter/lib/src/material/material_app.dart index 79f9ba486b4..52f98363042 100644 --- a/packages/flutter/lib/src/material/material_app.dart +++ b/packages/flutter/lib/src/material/material_app.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:async'; import 'dart:ui' as ui; import 'package:flutter/rendering.dart'; @@ -37,13 +38,16 @@ class RouteArguments { } typedef Widget RouteBuilder(RouteArguments args); +typedef Future LocaleChangedCallback(ui.Locale locale); + class MaterialApp extends StatefulComponent { MaterialApp({ Key key, this.title, this.theme, this.routes: const {}, - this.onGenerateRoute + this.onGenerateRoute, + this.onLocaleChanged }) : super(key: key) { assert(routes != null); assert(routes.containsKey(Navigator.defaultRouteName) || onGenerateRoute != null); @@ -53,6 +57,7 @@ class MaterialApp extends StatefulComponent { final ThemeData theme; final Map routes; final RouteFactory onGenerateRoute; + final LocaleChangedCallback onLocaleChanged; _MaterialAppState createState() => new _MaterialAppState(); } @@ -62,11 +67,13 @@ class _MaterialAppState extends State implements BindingObserver { GlobalObjectKey _navigator; Size _size; + LocaleQueryData _localeData; void initState() { super.initState(); _navigator = new GlobalObjectKey(this); _size = ui.window.size; + didChangeLocale(ui.window.locale); FlutterBinding.instance.addObserver(this); } @@ -88,6 +95,15 @@ class _MaterialAppState extends State implements BindingObserver { 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(); Route _generateRoute(NamedRouteSettings settings) { @@ -106,23 +122,32 @@ class _MaterialAppState extends State implements BindingObserver { } 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(); return new MediaQuery( data: new MediaQueryData(size: _size), - child: new Theme( - data: theme, - child: new DefaultTextStyle( - style: _errorTextStyle, - child: new DefaultAssetBundle( - bundle: _defaultBundle, - child: new Title( - title: config.title, - color: theme.primaryColor, - child: new Navigator( - key: _navigator, - initialRoute: ui.window.defaultRouteName, - onGenerateRoute: _generateRoute, - observer: _heroController + child: new LocaleQuery( + data: _localeData, + child: new Theme( + data: theme, + child: new DefaultTextStyle( + style: _errorTextStyle, + child: new DefaultAssetBundle( + bundle: _defaultBundle, + child: new Title( + title: config.title, + color: theme.primaryColor, + child: new Navigator( + key: _navigator, + initialRoute: ui.window.defaultRouteName, + onGenerateRoute: _generateRoute, + observer: _heroController + ) ) ) ) diff --git a/packages/flutter/lib/src/rendering/binding.dart b/packages/flutter/lib/src/rendering/binding.dart index b5ba17192e4..34e6554eb79 100644 --- a/packages/flutter/lib/src/rendering/binding.dart +++ b/packages/flutter/lib/src/rendering/binding.dart @@ -197,6 +197,7 @@ class _PointerEventConverter { class BindingObserver { bool didPopRoute() => false; void didChangeSize(Size size) { } + void didChangeLocale(ui.Locale locale) { } } /// The glue between the render tree and the Flutter engine @@ -208,6 +209,7 @@ class FlutterBinding extends HitTestTarget { ui.window.onPointerPacket = _handlePointerPacket; ui.window.onMetricsChanged = _handleMetricsChanged; + ui.window.onLocaleChanged = _handleLocaleChanged; ui.window.onPopRoute = _handlePopRoute; if (renderViewOverride == null) { @@ -244,6 +246,11 @@ class FlutterBinding extends HitTestTarget { observer.didChangeSize(size); } + void _handleLocaleChanged() { + for (BindingObserver observer in _observers) + observer.didChangeLocale(ui.window.locale); + } + void _handlePersistentFrameCallback(Duration timeStamp) { beginFrame(); } diff --git a/packages/flutter/lib/src/widgets/locale_query.dart b/packages/flutter/lib/src/widgets/locale_query.dart new file mode 100644 index 00000000000..2bfbbf84124 --- /dev/null +++ b/packages/flutter/lib/src/widgets/locale_query.dart @@ -0,0 +1,32 @@ +// 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 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 description) { + super.debugFillDescription(description); + description.add('$data'); + } +} diff --git a/packages/flutter/lib/widgets.dart b/packages/flutter/lib/widgets.dart index 4ddc8cf1941..fc927218f29 100644 --- a/packages/flutter/lib/widgets.dart +++ b/packages/flutter/lib/widgets.dart @@ -19,6 +19,7 @@ export 'src/widgets/gesture_detector.dart'; export 'src/widgets/gridpaper.dart'; export 'src/widgets/heroes.dart'; export 'src/widgets/homogeneous_viewport.dart'; +export 'src/widgets/locale_query.dart'; export 'src/widgets/media_query.dart'; export 'src/widgets/mimic.dart'; export 'src/widgets/mixed_viewport.dart'; diff --git a/packages/flutter/pubspec.yaml b/packages/flutter/pubspec.yaml index e9563330587..ac4d94a810b 100644 --- a/packages/flutter/pubspec.yaml +++ b/packages/flutter/pubspec.yaml @@ -7,8 +7,8 @@ dependencies: collection: '>=1.1.3 <2.0.0' intl: '>=0.12.4+2 <0.13.0' material_design_icons: '>=0.0.3 <0.1.0' - sky_engine: 0.0.67 - sky_services: 0.0.67 + sky_engine: 0.0.68 + sky_services: 0.0.68 vector_math: '>=1.4.3 <2.0.0' # To pin the transitive dependency through mojo_sdk.