mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
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.
This commit is contained in:
parent
1e1761d181
commit
9693cd5537
@ -94,6 +94,14 @@ class StocksAppState extends State<StocksApp> {
|
||||
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) {
|
||||
return new MaterialApp(
|
||||
title: 'Stocks',
|
||||
@ -102,13 +110,12 @@ class StocksAppState extends State<StocksApp> {
|
||||
'/': (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());
|
||||
}
|
||||
|
||||
@ -147,7 +147,7 @@ class StockHomeState extends State<StockHome> {
|
||||
Widget buildToolBar() {
|
||||
return new ToolBar(
|
||||
elevation: 0,
|
||||
center: new Text(StockStrings.title()),
|
||||
center: new Text(StockStrings.of(context).title()),
|
||||
right: <Widget>[
|
||||
new IconButton(
|
||||
icon: "action/search",
|
||||
@ -161,8 +161,9 @@ class StockHomeState extends State<StockHome> {
|
||||
tabBar: new TabBar(
|
||||
selection: _tabBarSelection,
|
||||
labels: <TabLabel>[
|
||||
new TabLabel(text: StockStrings.market()),
|
||||
new TabLabel(text: StockStrings.portfolio())]
|
||||
new TabLabel(text: StockStrings.of(context).market()),
|
||||
new TabLabel(text: StockStrings.of(context).portfolio())
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ -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'
|
||||
|
||||
@ -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<LocaleQueryData> LocaleChangedCallback(ui.Locale locale);
|
||||
|
||||
class MaterialApp extends StatefulComponent {
|
||||
MaterialApp({
|
||||
Key key,
|
||||
this.title,
|
||||
this.theme,
|
||||
this.routes: const <String, RouteBuilder>{},
|
||||
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<String, RouteBuilder> routes;
|
||||
final RouteFactory onGenerateRoute;
|
||||
final LocaleChangedCallback onLocaleChanged;
|
||||
|
||||
_MaterialAppState createState() => new _MaterialAppState();
|
||||
}
|
||||
@ -62,11 +67,13 @@ class _MaterialAppState extends State<MaterialApp> 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<MaterialApp> 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<MaterialApp> 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
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
32
packages/flutter/lib/src/widgets/locale_query.dart
Normal file
32
packages/flutter/lib/src/widgets/locale_query.dart
Normal file
@ -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<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';
|
||||
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';
|
||||
|
||||
@ -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.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user