Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
8c2c5022
Commit
8c2c5022
authored
Aug 24, 2017
by
Hans Muller
Committed by
GitHub
Aug 24, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Updated I18N API for Flutter (#11497)
parent
7676e511
Changes
19
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
1061 additions
and
237 deletions
+1061
-237
regenerate.md
examples/stocks/lib/i18n/regenerate.md
+30
-7
stock_messages_all.dart
examples/stocks/lib/i18n/stock_messages_all.dart
+2
-1
main.dart
examples/stocks/lib/main.dart
+11
-10
stock_strings.dart
examples/stocks/lib/stock_strings.dart
+31
-18
locale_test.dart
examples/stocks/test/locale_test.dart
+10
-0
material.dart
packages/flutter/lib/material.dart
+1
-0
app.dart
packages/flutter/lib/src/material/app.dart
+39
-11
app_bar.dart
packages/flutter/lib/src/material/app_bar.dart
+2
-1
back_button.dart
packages/flutter/lib/src/material/back_button.dart
+3
-2
date_picker.dart
packages/flutter/lib/src/material/date_picker.dart
+3
-2
material_localizations.dart
...ages/flutter/lib/src/material/material_localizations.dart
+58
-0
app.dart
packages/flutter/lib/src/widgets/app.dart
+23
-28
binding.dart
packages/flutter/lib/src/widgets/binding.dart
+33
-17
locale_query.dart
packages/flutter/lib/src/widgets/locale_query.dart
+0
-45
localizations.dart
packages/flutter/lib/src/widgets/localizations.dart
+346
-0
widgets.dart
packages/flutter/lib/widgets.dart
+1
-1
date_picker_test.dart
packages/flutter/test/material/date_picker_test.dart
+56
-62
locale_query_test.dart
packages/flutter/test/widgets/locale_query_test.dart
+0
-32
localizations_test.dart
packages/flutter/test/widgets/localizations_test.dart
+412
-0
No files found.
examples/stocks/lib/i18n/regenerate.md
View file @
8c2c5022
To rebuild the i18n files:
## Regenerating the i18n files
The files in this directory are based on ../lib/stock_strings.dart
which defines all of the localizable strings used by the stocks
app. The stocks app uses
the
[
Dart `intl` package
](
https://github.com/dart-lang/intl
)
.
Rebuilding everything requires two steps.
With the
`examples/stocks`
as the current directory, generate
`intl_messages.arb`
from
`lib/stock_strings.dart`
:
```
pub run intl_translation:generate_from_arb \
--output-dir=lib/i18n \
--generated-file-prefix=stock_ \
--no-use-deferred-loading \
lib/*.dart \
lib/i18n/*.arb
flutter pub pub run intl_translation:extract_to_arb --output-dir=lib/i18n lib/stock_strings.dart
```
The
`intl_messages.arb`
file is a JSON format map with one entry for
each
`Intl.message()`
function defined in
`stock_strings.dart`
. This
file was used to create the English and Spanish localizations,
`stocks_en.arb`
and
`stocks_es.arb`
. The
`intl_messages.arb`
wasn't
checked into the repository, since it only serves as a template for
the other
`.arb`
files.
With the
`examples/stocks`
as the current directory, generate a
`stock_messages_<locale>.dart`
for each
`stocks_<locale>.arb`
file and
`stock_messages_all.dart`
, which imports all of the messages files:
```
flutter pub pub run intl_translation:generate_from_arb --output-dir=lib/i18n \
--generated-file-prefix=stock_ --no-use-deferred-loading lib/*.dart lib/i18n/stocks_*.arb
```
The
`StockStrings`
class uses the generated
`initializeMessages()`
function (
`stock_messages_all.dart`
) to load the localized messages
and
`Intl.message()`
to look them up.
examples/stocks/lib/i18n/stock_messages_all.dart
View file @
8c2c5022
...
...
@@ -11,7 +11,8 @@ import 'package:intl/src/intl_helpers.dart';
import
'stock_messages_en.dart'
as
messages_en
;
import
'stock_messages_es.dart'
as
messages_es
;
Map
<
String
,
Function
>
_deferredLibraries
=
{
typedef
Future
<
dynamic
>
LibraryLoader
();
Map
<
String
,
LibraryLoader
>
_deferredLibraries
=
{
'en'
:
()
=>
new
Future
.
value
(
null
),
'es'
:
()
=>
new
Future
.
value
(
null
),
};
...
...
examples/stocks/lib/main.dart
View file @
8c2c5022
...
...
@@ -13,9 +13,7 @@ import 'package:flutter/rendering.dart' show
debugPaintLayerBordersEnabled
,
debugPaintPointersEnabled
,
debugRepaintRainbowEnabled
;
import
'package:intl/intl.dart'
;
import
'i18n/stock_messages_all.dart'
;
import
'stock_data.dart'
;
import
'stock_home.dart'
;
import
'stock_settings.dart'
;
...
...
@@ -23,6 +21,14 @@ import 'stock_strings.dart';
import
'stock_symbol_viewer.dart'
;
import
'stock_types.dart'
;
class
_StocksLocalizationsDelegate
extends
LocalizationsDelegate
<
StockStrings
>
{
@override
Future
<
StockStrings
>
load
(
Locale
locale
)
=>
StockStrings
.
load
(
locale
);
@override
bool
shouldReload
(
_StocksLocalizationsDelegate
old
)
=>
false
;
}
class
StocksApp
extends
StatefulWidget
{
@override
StocksAppState
createState
()
=>
new
StocksAppState
();
...
...
@@ -99,13 +105,6 @@ class StocksAppState extends State<StocksApp> {
return
null
;
}
Future
<
LocaleQueryData
>
_onLocaleChanged
(
Locale
locale
)
async
{
final
String
localeString
=
locale
.
toString
();
await
initializeMessages
(
localeString
);
Intl
.
defaultLocale
=
localeString
;
return
StockStrings
.
instance
;
}
@override
Widget
build
(
BuildContext
context
)
{
assert
(()
{
...
...
@@ -119,6 +118,9 @@ class StocksAppState extends State<StocksApp> {
return
new
MaterialApp
(
title:
'Stocks'
,
theme:
theme
,
localizationsDelegates:
<
_StocksLocalizationsDelegate
>[
new
_StocksLocalizationsDelegate
(),
],
debugShowMaterialGrid:
_configuration
.
debugShowGrid
,
showPerformanceOverlay:
_configuration
.
showPerformanceOverlay
,
showSemanticsDebugger:
_configuration
.
showSemanticsDebugger
,
...
...
@@ -127,7 +129,6 @@ class StocksAppState extends State<StocksApp> {
'/settings'
:
(
BuildContext
context
)
=>
new
StockSettings
(
_configuration
,
configurationUpdater
)
},
onGenerateRoute:
_getRoute
,
onLocaleChanged:
_onLocaleChanged
);
}
}
...
...
examples/stocks/lib/stock_strings.dart
View file @
8c2c5022
...
...
@@ -2,39 +2,52 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:async'
;
import
'package:intl/intl.dart'
;
import
'package:flutter/widgets.dart'
;
// Wrappers for strings that are shown in the UI. The strings can be
// translated for different locales using the Dart intl package.
//
// Locale-specific values for the strings live in the i18n/*.arb files.
//
// 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
import
'i18n/stock_messages_all.dart'
;
class
StockStrings
extends
LocaleQueryData
{
static
StockStrings
of
(
BuildContext
context
)
{
return
LocaleQuery
.
of
(
context
);
// Information about how this file relates to i18n/stock_messages_all.dart and how the i18n files
// were generated can be found in i18n/regenerate.md.
class
StockStrings
{
StockStrings
(
Locale
locale
)
:
_localeName
=
locale
.
toString
();
final
String
_localeName
;
static
Future
<
StockStrings
>
load
(
Locale
locale
)
{
return
initializeMessages
(
locale
.
toString
())
.
then
((
Null
_
)
{
return
new
StockStrings
(
locale
);
});
}
static
final
StockStrings
instance
=
new
StockStrings
();
static
StockStrings
of
(
BuildContext
context
)
{
return
Localizations
.
of
<
StockStrings
>(
context
,
StockStrings
);
}
String
title
()
=>
Intl
.
message
(
'Stocks'
,
name:
'title'
,
desc:
'Title for the Stocks application'
);
String
title
()
{
return
Intl
.
message
(
'<Stocks>'
,
name:
'title'
,
desc:
'Title for the Stocks application'
,
locale:
_localeName
,
);
}
String
market
()
=>
Intl
.
message
(
'MARKET'
,
name:
'market'
,
desc:
'Label for the Market tab'
desc:
'Label for the Market tab'
,
locale:
_localeName
,
);
String
portfolio
()
=>
Intl
.
message
(
'PORTFOLIO'
,
name:
'portfolio'
,
desc:
'Label for the Portfolio tab'
desc:
'Label for the Portfolio tab'
,
locale:
_localeName
,
);
}
examples/stocks/test/locale_test.dart
View file @
8c2c5022
...
...
@@ -13,9 +13,19 @@ void main() {
stocks
.
main
();
await
tester
.
idle
();
// see https://github.com/flutter/flutter/issues/1865
await
tester
.
pump
();
// The initial test app's locale is "_", so we're seeing the fallback translation here.
expect
(
find
.
text
(
'MARKET'
),
findsOneWidget
);
await
tester
.
binding
.
setLocale
(
'es'
,
'US'
);
await
tester
.
idle
();
// The Localizations widget has been built with the new locale. The
// new locale's strings are loaded asynchronously, so we're still
// displaying the previous locale's strings.
await
tester
.
pump
();
expect
(
find
.
text
(
'MARKET'
),
findsOneWidget
);
// The localized strings have finished loading and dependent
// widgets have been updated.
await
tester
.
pump
();
expect
(
find
.
text
(
'MERCADO'
),
findsOneWidget
);
});
...
...
packages/flutter/lib/material.dart
View file @
8c2c5022
...
...
@@ -57,6 +57,7 @@ export 'src/material/ink_well.dart';
export
'src/material/input_decorator.dart'
;
export
'src/material/list_tile.dart'
;
export
'src/material/material.dart'
;
export
'src/material/material_localizations.dart'
;
export
'src/material/mergeable_material.dart'
;
export
'src/material/page.dart'
;
export
'src/material/paginated_data_table.dart'
;
...
...
packages/flutter/lib/src/material/app.dart
View file @
8c2c5022
...
...
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'dart:async'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/widgets.dart'
;
...
...
@@ -10,11 +12,10 @@ import 'arc.dart';
import
'colors.dart'
;
import
'floating_action_button.dart'
;
import
'icons.dart'
;
import
'material_localizations.dart'
;
import
'page.dart'
;
import
'theme.dart'
;
export
'dart:ui'
show
Locale
;
const
TextStyle
_errorTextStyle
=
const
TextStyle
(
color:
const
Color
(
0xD0FF0000
),
fontFamily:
'monospace'
,
...
...
@@ -25,6 +26,16 @@ const TextStyle _errorTextStyle = const TextStyle(
decorationStyle:
TextDecorationStyle
.
double
);
class
_MaterialLocalizationsDelegate
extends
LocalizationsDelegate
<
MaterialLocalizations
>
{
const
_MaterialLocalizationsDelegate
();
@override
Future
<
MaterialLocalizations
>
load
(
Locale
locale
)
=>
MaterialLocalizations
.
load
(
locale
);
@override
bool
shouldReload
(
_MaterialLocalizationsDelegate
old
)
=>
false
;
}
/// An application that uses material design.
///
/// A convenience widget that wraps a number of widgets that are commonly
...
...
@@ -80,7 +91,8 @@ class MaterialApp extends StatefulWidget {
this
.
initialRoute
,
this
.
onGenerateRoute
,
this
.
onUnknownRoute
,
this
.
onLocaleChanged
,
this
.
locale
,
this
.
localizationsDelegates
,
this
.
navigatorObservers
:
const
<
NavigatorObserver
>[],
this
.
debugShowMaterialGrid
:
false
,
this
.
showPerformanceOverlay
:
false
,
...
...
@@ -129,10 +141,9 @@ class MaterialApp extends StatefulWidget {
/// normally, unless [initialRoute] is specified. It's also the route that's
/// displayed if the [initialRoute] can't be displayed.
///
/// To be able to directly call [Theme.of], [MediaQuery.of],
/// [LocaleQuery.of], etc, in the code sets the [home] argument in
/// the constructor, you can use a [Builder] widget to get a
/// [BuildContext].
/// To be able to directly call [Theme.of], [MediaQuery.of], etc, in the code
/// that sets the [home] argument in the constructor, you can use a [Builder]
/// widget to get a [BuildContext].
///
/// If [home] is specified, then [routes] must not include an entry for `/`,
/// as [home] takes its place.
...
...
@@ -210,9 +221,16 @@ class MaterialApp extends StatefulWidget {
/// message.
final
RouteFactory
onUnknownRoute
;
/// Callback that is called when the operating system changes the
/// current locale.
final
LocaleChangedCallback
onLocaleChanged
;
/// The initial locale for this app's [Localizations] widget.
///
/// If the `locale` is null the system's locale value is used.
final
Locale
locale
;
/// The delegates for this app's [Localizations] widget.
///
/// The delegates collectively define all of the localized resources
/// for this application's [Localizations] widget.
final
Iterable
<
LocalizationsDelegate
<
dynamic
>>
localizationsDelegates
;
/// Turns on a performance overlay.
///
...
...
@@ -297,6 +315,14 @@ class _MaterialAppState extends State<MaterialApp> {
_heroController
=
new
HeroController
(
createRectTween:
_createRectTween
);
}
// Combine the Localizations for Material with the ones contributed
// by the localizationsDelegates parameter, if any.
Iterable
<
LocalizationsDelegate
<
dynamic
>>
_createLocalizationsDelegates
()
sync
*
{
yield
const
_MaterialLocalizationsDelegate
();
if
(
widget
.
localizationsDelegates
!=
null
)
yield
*
widget
.
localizationsDelegates
;
}
RectTween
_createRectTween
(
Rect
begin
,
Rect
end
)
{
return
new
MaterialRectArcTween
(
begin:
begin
,
end:
end
);
}
...
...
@@ -319,6 +345,7 @@ class _MaterialAppState extends State<MaterialApp> {
return
null
;
}
Route
<
dynamic
>
_onUnknownRoute
(
RouteSettings
settings
)
{
assert
(()
{
if
(
widget
.
onUnknownRoute
==
null
)
{
...
...
@@ -369,7 +396,8 @@ class _MaterialAppState extends State<MaterialApp> {
initialRoute:
widget
.
initialRoute
,
onGenerateRoute:
_onGenerateRoute
,
onUnknownRoute:
_onUnknownRoute
,
onLocaleChanged:
widget
.
onLocaleChanged
,
locale:
widget
.
locale
,
localizationsDelegates:
_createLocalizationsDelegates
(),
showPerformanceOverlay:
widget
.
showPerformanceOverlay
,
checkerboardRasterCacheImages:
widget
.
checkerboardRasterCacheImages
,
checkerboardOffscreenLayers:
widget
.
checkerboardOffscreenLayers
,
...
...
packages/flutter/lib/src/material/app_bar.dart
View file @
8c2c5022
...
...
@@ -15,6 +15,7 @@ import 'flexible_space_bar.dart';
import
'icon_button.dart'
;
import
'icons.dart'
;
import
'material.dart'
;
import
'material_localizations.dart'
;
import
'page.dart'
;
import
'scaffold.dart'
;
import
'tabs.dart'
;
...
...
@@ -352,7 +353,7 @@ class _AppBarState extends State<AppBar> {
leading
=
new
IconButton
(
icon:
const
Icon
(
Icons
.
menu
),
onPressed:
_handleDrawerButton
,
tooltip:
'Open navigation menu'
// TODO(ianh): Figure out how to localize this string
tooltip:
MaterialLocalizations
.
of
(
context
).
openAppDrawerTooltip
,
);
}
else
{
if
(
canPop
)
...
...
packages/flutter/lib/src/material/back_button.dart
View file @
8c2c5022
...
...
@@ -6,6 +6,7 @@ import 'package:flutter/widgets.dart';
import
'icon_button.dart'
;
import
'icons.dart'
;
import
'material_localizations.dart'
;
import
'theme.dart'
;
/// A "back" icon that's appropriate for the current [TargetPlatform].
...
...
@@ -83,7 +84,7 @@ class BackButton extends StatelessWidget {
return
new
IconButton
(
icon:
const
BackButtonIcon
(),
color:
color
,
tooltip:
'Back'
,
// TODO(ianh): Figure out how to localize this string
tooltip:
MaterialLocalizations
.
of
(
context
).
backButtonTooltip
,
onPressed:
()
{
Navigator
.
of
(
context
).
maybePop
();
}
...
...
@@ -115,7 +116,7 @@ class CloseButton extends StatelessWidget {
Widget
build
(
BuildContext
context
)
{
return
new
IconButton
(
icon:
const
Icon
(
Icons
.
close
),
tooltip:
'Close'
,
// TODO(ianh): Figure out how to localize this string
tooltip:
MaterialLocalizations
.
of
(
context
).
closeButtonTooltip
,
onPressed:
()
{
Navigator
.
of
(
context
).
maybePop
();
},
...
...
packages/flutter/lib/src/material/date_picker.dart
View file @
8c2c5022
...
...
@@ -23,6 +23,7 @@ import 'icon_button.dart';
import
'icons.dart'
;
import
'ink_well.dart'
;
import
'material.dart'
;
import
'material_localizations.dart'
;
import
'theme.dart'
;
import
'typography.dart'
;
...
...
@@ -548,7 +549,7 @@ class _MonthPickerState extends State<MonthPicker> {
left:
8.0
,
child:
new
IconButton
(
icon:
const
Icon
(
Icons
.
chevron_left
),
tooltip:
'Previous month'
,
tooltip:
MaterialLocalizations
.
of
(
context
).
previousMonthTooltip
,
onPressed:
_isDisplayingFirstMonth
?
null
:
_handlePreviousMonth
,
),
),
...
...
@@ -557,7 +558,7 @@ class _MonthPickerState extends State<MonthPicker> {
right:
8.0
,
child:
new
IconButton
(
icon:
const
Icon
(
Icons
.
chevron_right
),
tooltip:
'Next month'
,
tooltip:
MaterialLocalizations
.
of
(
context
).
nextMonthTooltip
,
onPressed:
_isDisplayingLastMonth
?
null
:
_handleNextMonth
,
),
),
...
...
packages/flutter/lib/src/material/material_localizations.dart
0 → 100644
View file @
8c2c5022
// 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
'dart:async'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/widgets.dart'
;
/// Default localized resource values for the material widgets.
///
/// This class is just a placeholder, it only provides English values.
class
MaterialLocalizations
{
const
MaterialLocalizations
.
_
(
this
.
locale
)
:
assert
(
locale
!=
null
);
/// The locale for which the values of this class's localized resources
/// have been translated.
final
Locale
locale
;
/// Creates an object that provides default localized resource values for the
/// for the widgets of the material library.
///
/// This method is typically used to create a [DefaultLocalizationsDelegate].
/// The [MaterialApp] does so by default.
static
Future
<
MaterialLocalizations
>
load
(
Locale
locale
)
{
return
new
SynchronousFuture
<
MaterialLocalizations
>(
new
MaterialLocalizations
.
_
(
locale
));
}
/// The `MaterialLocalizations` from the closest [Localizations] instance
/// that encloses the given context.
///
/// This method is just a convenient shorthand for:
/// `Localizations.of<MaterialLocalizations>(context, MaterialLocalizations)`.
///
/// References to the localized resources defined by this class are typically
/// written in terms of this method. For example:
/// ```dart
/// tooltip: MaterialLocalizations.of(context).backButtonTooltip,
/// ```
static
MaterialLocalizations
of
(
BuildContext
context
)
{
return
Localizations
.
of
<
MaterialLocalizations
>(
context
,
MaterialLocalizations
);
}
/// The tooltip for the leading [AppBar] menu (aka 'hamburger') button
String
get
openAppDrawerTooltip
=>
'Open navigation menu'
;
/// The [BackButton]'s tooltip.
String
get
backButtonTooltip
=>
'Back'
;
/// The [CloseButton]'s tooltip.
String
get
closeButtonTooltip
=>
'Close'
;
/// The tooltip for the [MonthPicker]'s "next month" button.
String
get
nextMonthTooltip
=>
'Next month'
;
/// The tooltip for the [MonthPicker]'s "previous month" button.
String
get
previousMonthTooltip
=>
'Previous month'
;
}
packages/flutter/lib/src/widgets/app.dart
View file @
8c2c5022
...
...
@@ -11,9 +11,8 @@ import 'package:flutter/rendering.dart';
import
'banner.dart'
;
import
'basic.dart'
;
import
'binding.dart'
;
import
'container.dart'
;
import
'framework.dart'
;
import
'local
e_query
.dart'
;
import
'local
izations
.dart'
;
import
'media_query.dart'
;
import
'navigator.dart'
;
import
'performance_overlay.dart'
;
...
...
@@ -22,10 +21,7 @@ import 'text.dart';
import
'title.dart'
;
import
'widget_inspector.dart'
;
/// Signature for a function that is called when the operating system changes the current locale.
///
/// Used by [WidgetsApp.onLocaleChanged].
typedef
Future
<
LocaleQueryData
>
LocaleChangedCallback
(
Locale
locale
);
export
'dart:ui'
show
Locale
;
/// A convenience class that wraps a number of widgets that are commonly
/// required for an application.
...
...
@@ -34,7 +30,7 @@ typedef Future<LocaleQueryData> LocaleChangedCallback(Locale locale);
/// back button to popping the [Navigator] or quitting the application.
///
/// See also: [CheckedModeBanner], [DefaultTextStyle], [MediaQuery],
/// [Local
eQuery
], [Title], [Navigator], [Overlay], [SemanticsDebugger] (the
/// [Local
izations
], [Title], [Navigator], [Overlay], [SemanticsDebugger] (the
/// widgets wrapped by this one).
///
/// The [onGenerateRoute] argument is required, and corresponds to
...
...
@@ -54,7 +50,8 @@ class WidgetsApp extends StatefulWidget {
@required
this
.
color
,
this
.
navigatorObservers
:
const
<
NavigatorObserver
>[],
this
.
initialRoute
,
this
.
onLocaleChanged
,
this
.
locale
,
this
.
localizationsDelegates
,
this
.
showPerformanceOverlay
:
false
,
this
.
checkerboardRasterCacheImages
:
false
,
this
.
checkerboardOffscreenLayers
:
false
,
...
...
@@ -130,9 +127,16 @@ class WidgetsApp extends StatefulWidget {
/// * [Navigator.pop], for removing a route from the stack.
final
String
initialRoute
;
/// Callback that is called when the operating system changes the
/// current locale.
final
LocaleChangedCallback
onLocaleChanged
;
/// The initial locale for this app's [Localizations] widget.
///
/// If the 'locale' is null the system's locale value is used.
final
Locale
locale
;
/// The delegates for this app's [Localizations] widget.
///
/// The delegates collectively define all of the localized resources
/// for this application's [Localizations] widget.
final
Iterable
<
LocalizationsDelegate
<
dynamic
>>
localizationsDelegates
;
/// Turns on a performance overlay.
/// https://flutter.io/debugging/#performanceoverlay
...
...
@@ -214,13 +218,13 @@ class WidgetsApp extends StatefulWidget {
class
_WidgetsAppState
extends
State
<
WidgetsApp
>
implements
WidgetsBindingObserver
{
GlobalObjectKey
<
NavigatorState
>
_navigator
;
Locale
QueryData
_localeData
;
Locale
_locale
;
@override
void
initState
()
{
super
.
initState
();
_navigator
=
new
GlobalObjectKey
<
NavigatorState
>(
this
);
didChangeLocale
(
ui
.
window
.
locale
)
;
_locale
=
ui
.
window
.
locale
;
WidgetsBinding
.
instance
.
addObserver
(
this
);
}
...
...
@@ -258,10 +262,9 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
@override
void
didChangeLocale
(
Locale
locale
)
{
if
(
widget
.
onLocaleChanged
!=
null
)
{
widget
.
onLocaleChanged
(
locale
).
then
<
Null
>((
LocaleQueryData
data
)
{
if
(
mounted
)
setState
(()
{
_localeData
=
data
;
});
if
(
locale
!=
_locale
)
{
setState
(()
{
_locale
=
locale
;
});
}
}
...
...
@@ -274,19 +277,11 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
@override
Widget
build
(
BuildContext
context
)
{
if
(
widget
.
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.
// TODO(ianh): Make this unnecessary. See https://github.com/flutter/flutter/issues/1865
// TODO(ianh): The following line should not be included in release mode, only in profile and debug modes.
WidgetsBinding
.
instance
.
preventThisFrameFromBeingReportedAsFirstFrame
();
return
new
Container
();
}
Widget
result
=
new
MediaQuery
(
data:
new
MediaQueryData
.
fromWindow
(
ui
.
window
),
child:
new
LocaleQuery
(
data:
_localeData
,
child:
new
Localizations
(
locale:
widget
.
locale
??
_locale
,
delegates:
widget
.
localizationsDelegates
,
child:
new
Title
(
title:
widget
.
title
,
color:
widget
.
color
,
...
...
packages/flutter/lib/src/widgets/binding.dart
View file @
8c2c5022
...
...
@@ -389,19 +389,35 @@ abstract class WidgetsBinding extends BindingBase with GestureBinding, RendererB
}
bool
_needToReportFirstFrame
=
true
;
bool
_thisFrameWasUseful
=
true
;
int
_deferFirstFrameReportCount
=
0
;
bool
get
_reportFirstFrame
=>
_deferFirstFrameReportCount
==
0
;
/// Tell the framework
that the frame we are currently building
///
should not be considered to be a useful first frame
.
/// Tell the framework
not to report the frame it is building as a "useful"
///
first frame until there is a corresponding call to [allowFirstFrameReport]
.
///
/// This is used by [WidgetsApp] to report the first frame.
//
// TODO(ianh): This method should only be available in debug and profile modes.
void
preventThisFrameFromBeingReportedAsFirstFrame
()
{
_thisFrameWasUseful
=
false
;
void
deferFirstFrameReport
()
{
assert
(
_deferFirstFrameReportCount
>=
0
);
_deferFirstFrameReportCount
+=
1
;
}
void
_handleBuildScheduled
()
{
/// When called after [deferFirstFrameReport]: tell the framework to report
/// the frame it is building as a "useful" first frame.
///
/// This method may only be called once for each corresponding call
/// to [deferFirstFrameReport].
///
/// This is used by [WidgetsApp] to report the first frame.
//
// TODO(ianh): This method should only be available in debug and profile modes.
void
allowFirstFrameReport
()
{
assert
(
_deferFirstFrameReportCount
>=
1
);
_deferFirstFrameReportCount
-=
1
;
}
void
_handleBuildScheduled
()
{
// If we're in the process of building dirty elements, then changes
// should not trigger a new frame.
assert
(()
{
...
...
@@ -522,14 +538,10 @@ abstract class WidgetsBinding extends BindingBase with GestureBinding, RendererB
}
// TODO(ianh): Following code should not be included in release mode, only profile and debug modes.
// See https://github.com/dart-lang/sdk/issues/27192
if
(
_needToReportFirstFrame
)
{
if
(
_thisFrameWasUseful
)
{
developer
.
Timeline
.
instantSync
(
'Widgets completed first useful frame'
);
developer
.
postEvent
(
'Flutter.FirstFrame'
,
<
String
,
dynamic
>{});
_needToReportFirstFrame
=
false
;
}
else
{
_thisFrameWasUseful
=
true
;
}
if
(
_needToReportFirstFrame
&&
_reportFirstFrame
)
{
developer
.
Timeline
.
instantSync
(
'Widgets completed first useful frame'
);
developer
.
postEvent
(
'Flutter.FirstFrame'
,
<
String
,
dynamic
>{});
_needToReportFirstFrame
=
false
;
}
}
...
...
@@ -556,11 +568,15 @@ abstract class WidgetsBinding extends BindingBase with GestureBinding, RendererB
@override
Future
<
Null
>
performReassemble
()
{
_needToReportFirstFrame
=
true
;
preventThisFrameFromBeingReportedAsFirstFrame
();
deferFirstFrameReport
();
if
(
renderViewElement
!=
null
)
buildOwner
.
reassemble
(
renderViewElement
);
return
super
.
performReassemble
();
// TODO(hansmuller): eliminate the value variable after analyzer bug
// https://github.com/flutter/flutter/issues/11646 is fixed.
final
Future
<
Null
>
value
=
super
.
performReassemble
();
return
value
.
then
((
Null
_
)
{
allowFirstFrameReport
();
});
}
}
...
...
packages/flutter/lib/src/widgets/locale_query.dart
deleted
100644 → 0
View file @
7676e511
// 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
'package:flutter/foundation.dart'
;
import
'framework.dart'
;
/// Superclass for locale-specific data provided by the application.
class
LocaleQueryData
{
}
// TODO(ianh): We need a better type here. This doesn't really make sense.
/// Establishes a subtree in which locale queries resolve to the given data.
class
LocaleQuery
extends
InheritedWidget
{
/// Creates a widget that provides [LocaleQueryData] to its descendants.
const
LocaleQuery
({
Key
key
,
@required
this
.
data
,
@required
Widget
child
})
:
assert
(
child
!=
null
),
super
(
key:
key
,
child:
child
);
/// The locale data for this subtree.
final
LocaleQueryData
data
;
/// The data from the closest instance of this class that encloses the given context.
///
/// Typical usage is as follows:
///
/// ```dart
/// MyLocaleData data = LocaleQueryData.of(context);
/// ```
static
LocaleQueryData
of
(
BuildContext
context
)
{
final
LocaleQuery
query
=
context
.
inheritFromWidgetOfExactType
(
LocaleQuery
);
return
query
?.
data
;
}
@override
bool
updateShouldNotify
(
LocaleQuery
old
)
=>
data
!=
old
.
data
;
@override
void
debugFillProperties
(
DiagnosticPropertiesBuilder
description
)
{
super
.
debugFillProperties
(
description
);
description
.
add
(
new
DiagnosticsProperty
<
LocaleQueryData
>(
'data'
,
data
,
showName:
false
));
}
}
packages/flutter/lib/src/widgets/localizations.dart
0 → 100644
View file @
8c2c5022
// 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
'dart:async'
;
import
'dart:ui'
show
Locale
;
import
'package:flutter/foundation.dart'
;
import
'binding.dart'
;
import
'container.dart'
;
import
'framework.dart'
;
// Examples can assume:
// class Intl { static String message(String s, { String name, String locale }) => ''; }
// Future<Null> initializeMessages(String locale) => null;
// A utility function used by Localizations to generate one future
// that completes when all of the LocalizationsDelegate.load() futures
// complete. The returned map is indexed by the type of each input
// future's value.
//
// The input future values must have distinct types.
//
// The returned Future<Map> will resolve when all of the input map's
// future values have resolved. If all of the input map's values are
// SynchronousFutures then a SynchronousFuture will be returned
// immediately.
//
// This is more complicated than just applying Future.wait to input
// because some of the input.values may be SynchronousFutures. We don't want
// to Future.wait for the synchronous futures.
Future
<
Map
<
Type
,
dynamic
>>
_loadAll
(
Iterable
<
Future
<
dynamic
>>
inputValues
)
{
final
Map
<
Type
,
dynamic
>
output
=
<
Type
,
dynamic
>{};
List
<
Future
<
dynamic
>>
outputFutures
;
for
(
Future
<
dynamic
>
inputValue
in
inputValues
)
{
dynamic
completedValue
;
final
Future
<
dynamic
>
futureValue
=
inputValue
.
then
<
dynamic
>((
dynamic
value
)
{
return
completedValue
=
value
;
});
if
(
completedValue
!=
null
)
{
// inputValue was a SynchronousFuture
final
Type
type
=
completedValue
.
runtimeType
;
assert
(!
output
.
containsKey
(
type
));
output
[
type
]
=
completedValue
;
}
else
{
outputFutures
??=
<
Future
<
dynamic
>>[];
outputFutures
.
add
(
futureValue
);
}
}
// All of the input.values were synchronous futures, we're done.
if
(
outputFutures
==
null
)
return
new
SynchronousFuture
<
Map
<
Type
,
dynamic
>>(
output
);
// Some of input.values were asynchronous futures. Wait for them.
return
Future
.
wait
<
dynamic
>(
outputFutures
).
then
<
Map
<
Type
,
dynamic
>>((
List
<
dynamic
>
values
)
{
for
(
dynamic
value
in
values
)
{
final
Type
type
=
value
.
runtimeType
;
assert
(!
output
.
containsKey
(
type
));
output
[
type
]
=
value
;
}
return
output
;
});
}
/// A factory for a set of localized resources of type `T`, to be loaded by a
/// [Localizations] widget.
///
/// Typical applications have one [Localizations] widget which is
/// created by the [WidgetsApp] and configured with the app's
/// `localizationsDelegates` parameter.
abstract
class
LocalizationsDelegate
<
T
>
{
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const
LocalizationsDelegate
();
/// Start loading the resources for `locale`. The returned future completes
/// when the resources have finished loading.
///
/// It's assumed that the this method will return an object that contains
/// a collection of related resources (typically defined with one method per
/// resource). The object will be retrieved with [Localizations.of].
Future
<
T
>
load
(
Locale
locale
);
/// Returns true if the resources for this delegate should be loaded
/// again by calling the [load] method.
///
/// This method is called whenever its [Localizations] widget is
/// rebuilt. If it returns true then dependent widgets will be rebuilt
/// after [load] has completed.
bool
shouldReload
(
covariant
LocalizationsDelegate
<
T
>
old
);
}
class
_LocalizationsScope
extends
InheritedWidget
{
_LocalizationsScope
({
Key
key
,
@required
this
.
locale
,
@required
this
.
localizationsState
,
Widget
child
,
})
:
super
(
key:
key
,
child:
child
)
{
assert
(
localizationsState
!=
null
);
}
final
Locale
locale
;
final
_LocalizationsState
localizationsState
;
@override
bool
updateShouldNotify
(
_LocalizationsScope
old
)
{
// Changes in Localizations.locale trigger a load(), see _LocalizationsState.didUpdateWidget()
return
false
;
}
}
/// Defines the [Locale] for its `child` and the localized resources that the
/// child depends on.
///
/// Localized resources are loaded by the list of [LocalizationsDelegate]
/// `delegates`. Each delegate is essentially a factory for a collection
/// of localized resources. There are multiple delegates because there are
/// multiple sources for localizations within an app.
///
/// Delegates are typically simple subclasses of [LocalizationsDelegate] that
/// override [LocalizationsDelegate.load]. For example a delegate for the
/// `MyLocalizations` class defined below would be:
///
/// ```dart
/// class _MyDelegate extends LocalizationsDelegate<MyLocalizations> {
/// @override
/// Future<MyLocalizations> load(Locale locale) => MyLocalizations.load(locale);
///}
/// ```
///
/// Each delegate can be viewed as a factory for objects that encapsulate a
/// a set of localized resources. These objects are retrieved with
/// by runtime type with [Localizations.of].
///
/// The [WidgetsApp] class creates a `Localizations` widget so most apps
/// will not need to create one. The widget app's `Localizations` delegates can
/// be initialized with [WidgetsApp.localizationsDelegates]. The [MaterialApp]
/// class also provides a `localizationsDelegates` parameter that's just
/// passed along to the [WidgetsApp].
///
/// Apps should retrieve collections of localized resources with
/// `Localizations.of<MyLocalizations>(context, MyLocalizations)`,
/// where MyLocalizations is an app specific class defines one function per
/// resource. This is conventionally done by a static `.of` method on the
/// MyLocalizations class.
///
/// For example, using the `MyLocalizations` class defined below, one would
/// lookup a localized title string like this:
/// ```dart
/// MyLocalizations.of(context).title()
/// ```
/// If `Localizations` were to be rebuilt with a new `locale` then
/// the widget subtree that corresponds to [BuildContext] `context` would
/// be rebuilt after the corresponding resources had been loaded.
///
/// This class is effectively an [InheritedWidget]. If it's rebuilt with
/// a new `locale` or a different list of delegates or any of its
/// delegates' [LocalizationDelegate.shouldReload()] methods returns true,
/// then widgets that have created a dependency by calling
/// `Localizations.of(context)` will be rebuilt after the resources
/// for the new locale have been loaded.
///
/// ## Sample code
///
/// This following class is defined in terms of the
/// [Dart `intl` package](https://github.com/dart-lang/intl). Using the `intl`
/// package isn't required.
///
/// ```dart
/// class MyLocalizations {
/// MyLocalizations(this.locale);
///
/// final Locale locale;
///
/// static Future<MyLocalizations> load(Locale locale) {
/// return initializeMessages(locale.toString())
/// .then((Null _) {
/// return new MyLocalizations(locale);
/// });
/// }
///
/// static MyLocalizations of(BuildContext context) {
/// return Localizations.of<MyLocalizations>(context, MyLocalizations);
/// }
///
/// String title() => Intl.message('<title>', name: 'title', locale: locale.toString());
/// // ... more Intl.message() methods like title()
/// }
/// ```
/// A class based on the `intl` package imports a generated message catalog that provides
/// the `initializeMessages()` function and the per-locale backing store for `Intl.message()`.
/// The message catalog is produced by an `intl` tool that analyzes the source code for
/// classes that contain `Intl.message()` calls. In this case that would just be the
/// `MyLocalizations` class.
///
/// One could choose another approach for loading localized resources and looking them up while
/// still conforming to the structure of this example.
class
Localizations
extends
StatefulWidget
{
Localizations
({
Key
key
,
@required
this
.
locale
,
this
.
delegates
,
this
.
child
})
:
super
(
key:
key
)
{
assert
(
locale
!=
null
);
}
/// The resources returned by [Localizations.of] will be specific to this locale.
final
Locale
locale
;
/// This list collectively defines the localized resources objects that can
/// be retrieved with [ Localizations.of].
final
Iterable
<
LocalizationsDelegate
<
dynamic
>>
delegates
;
/// The widget below this widget in the tree.
final
Widget
child
;
/// The locale of the Localizations widget for the widget tree that
/// corresponds to [BuildContext] `context`.
static
Locale
localeOf
(
BuildContext
context
)
{
assert
(
context
!=
null
);
final
_LocalizationsScope
scope
=
context
.
inheritFromWidgetOfExactType
(
_LocalizationsScope
);
return
scope
.
localizationsState
.
locale
;
}
/// Returns the 'type' localized resources for the widget tree that
/// corresponds to [BuildContext] `context`.
///
/// This method is typically used by a static factory method on the 'type'
/// class. For example Flutter's MaterialLocalizations class looks up Material
/// resources with a method defined like this:
///
/// ```dart
/// static MaterialLocalizations of(BuildContext context) {
/// return Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);
/// }
/// ```
static
T
of
<
T
>(
BuildContext
context
,
Type
type
)
{
assert
(
context
!=
null
);
assert
(
type
!=
null
);
final
_LocalizationsScope
scope
=
context
.
inheritFromWidgetOfExactType
(
_LocalizationsScope
);
return
scope
.
localizationsState
.
resourcesFor
<
T
>(
type
);
}
@override
_LocalizationsState
createState
()
=>
new
_LocalizationsState
();
}
class
_LocalizationsState
extends
State
<
Localizations
>
{
final
GlobalKey
_localizedResourcesScopeKey
=
new
GlobalKey
();
Map
<
Type
,
dynamic
>
_typeToResources
=
<
Type
,
dynamic
>{};
Locale
get
locale
=>
_locale
;
Locale
_locale
;
@override
void
initState
()
{
super
.
initState
();
load
(
widget
.
locale
);
}
bool
_anyDelegatesShouldReload
(
Localizations
old
)
{
if
(
widget
.
delegates
.
length
!=
old
.
delegates
.
length
)
return
true
;
final
List
<
LocalizationsDelegate
<
dynamic
>>
delegates
=
widget
.
delegates
.
toList
();
final
List
<
LocalizationsDelegate
<
dynamic
>>
oldDelegates
=
old
.
delegates
.
toList
();
for
(
int
i
=
0
;
i
<
delegates
.
length
;
i
+=
1
)
{
final
LocalizationsDelegate
<
dynamic
>
delegate
=
delegates
[
i
];
final
LocalizationsDelegate
<
dynamic
>
oldDelegate
=
oldDelegates
[
i
];
if
(
delegate
.
runtimeType
!=
oldDelegate
.
runtimeType
||
delegate
.
shouldReload
(
oldDelegate
))
return
true
;
}
return
false
;
}
@override
void
didUpdateWidget
(
Localizations
old
)
{
super
.
didUpdateWidget
(
old
);
if
(
widget
.
locale
!=
old
.
locale
||
(
widget
.
delegates
==
null
&&
old
.
delegates
!=
null
)
||
(
widget
.
delegates
!=
null
&&
old
.
delegates
==
null
)
||
(
widget
.
delegates
!=
null
&&
_anyDelegatesShouldReload
(
old
)))
load
(
widget
.
locale
);
}
void
load
(
Locale
locale
)
{
final
Iterable
<
LocalizationsDelegate
<
dynamic
>>
delegates
=
widget
.
delegates
;
if
(
delegates
==
null
||
delegates
.
isEmpty
)
{
_locale
=
locale
;
return
;
}
final
Iterable
<
Future
<
dynamic
>>
allResources
=
delegates
.
map
((
LocalizationsDelegate
<
dynamic
>
delegate
)
{
return
delegate
.
load
(
locale
);
});
Map
<
Type
,
dynamic
>
typeToResources
;
final
Future
<
Map
<
Type
,
dynamic
>>
typeToResourcesFuture
=
_loadAll
(
allResources
)
.
then
((
Map
<
Type
,
dynamic
>
value
)
{
return
typeToResources
=
value
;
});
if
(
typeToResources
!=
null
)
{
// All of the delegates' resources loaded synchronously.
_typeToResources
=
typeToResources
;
_locale
=
locale
;
}
else
{
// - Don't rebuild the dependent widgets until the resources for the new locale
// have finished loading. Until then the old locale will continue to be used.
// - If we're running at app startup time then defer reporting the first
// "useful" frame until after the async load has completed.
WidgetsBinding
.
instance
.
deferFirstFrameReport
();
typeToResourcesFuture
.
then
((
Map
<
Type
,
dynamic
>
value
)
{
WidgetsBinding
.
instance
.
allowFirstFrameReport
();
if
(!
mounted
)
return
;
setState
(()
{
_typeToResources
=
value
;
_locale
=
locale
;
});
final
InheritedElement
scopeElement
=
_localizedResourcesScopeKey
.
currentContext
;
scopeElement
?.
dispatchDidChangeDependencies
();
});
}
}
T
resourcesFor
<
T
>(
Type
type
)
{
assert
(
type
!=
null
);
final
dynamic
resources
=
_typeToResources
[
type
];
assert
(
resources
.
runtimeType
==
type
);
return
resources
;
}
@override
Widget
build
(
BuildContext
context
)
{
return
new
_LocalizationsScope
(
key:
_localizedResourcesScopeKey
,
locale:
widget
.
locale
,
localizationsState:
this
,
child:
_locale
!=
null
?
widget
.
child
:
new
Container
(),
);
}
}
packages/flutter/lib/widgets.dart
View file @
8c2c5022
...
...
@@ -45,7 +45,7 @@ export 'src/widgets/image.dart';
export
'src/widgets/image_icon.dart'
;
export
'src/widgets/implicit_animations.dart'
;
export
'src/widgets/layout_builder.dart'
;
export
'src/widgets/local
e_query
.dart'
;
export
'src/widgets/local
izations
.dart'
;
export
'src/widgets/media_query.dart'
;
export
'src/widgets/modal_barrier.dart'
;
export
'src/widgets/navigation_toolbar.dart'
;
...
...
packages/flutter/test/material/date_picker_test.dart
View file @
8c2c5022
...
...
@@ -28,94 +28,88 @@ void main() {
DateTime
_selectedDate
=
new
DateTime
(
2016
,
DateTime
.
JULY
,
26
);
await
tester
.
pumpWidget
(
new
Overlay
(
initialEntries:
<
OverlayEntry
>[
new
OverlayEntry
(
builder:
(
BuildContext
context
)
=>
new
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
return
new
Positioned
(
width:
400.0
,
child:
new
SingleChildScrollView
(
child:
new
Material
(
child:
new
MonthPicker
(
firstDate:
new
DateTime
(
0
),
lastDate:
new
DateTime
(
9999
),
key:
_datePickerKey
,
selectedDate:
_selectedDate
,
onChanged:
(
DateTime
value
)
{
setState
(()
{
_selectedDate
=
value
;
});
},
),
),
new
MaterialApp
(
home:
new
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
return
new
Container
(
width:
400.0
,
child:
new
SingleChildScrollView
(
child:
new
Material
(
child:
new
MonthPicker
(
firstDate:
new
DateTime
(
0
),
lastDate:
new
DateTime
(
9999
),
key:
_datePickerKey
,
selectedDate:
_selectedDate
,
onChanged:
(
DateTime
value
)
{
setState
(()
{
_selectedDate
=
value
;
});
},
),
)
;
}
,
)
,
)
,
]
,
)
,
)
,
)
,
)
;
}
,
)
,
)
);
expect
(
_selectedDate
,
equals
(
new
DateTime
(
2016
,
DateTime
.
JULY
,
26
)));
await
tester
.
tapAt
(
const
Offset
(
50.0
,
100.0
));
expect
(
_selectedDate
,
equals
(
new
DateTime
(
2016
,
DateTime
.
JULY
,
26
))
);
await
tester
.
pumpAndSettle
(
);
await
tester
.
pump
(
const
Duration
(
seconds:
2
));
await
tester
.
tapAt
(
const
Offset
(
300.0
,
100.0
));
await
tester
.
tap
(
find
.
text
(
'1'
));
await
tester
.
pumpAndSettle
();
expect
(
_selectedDate
,
equals
(
new
DateTime
(
2016
,
DateTime
.
JULY
,
1
)));
await
tester
.
pump
(
const
Duration
(
seconds:
2
));
await
tester
.
tap
At
(
const
Offset
(
380.0
,
20.0
));
await
tester
.
pumpAndSettle
(
const
Duration
(
milliseconds:
100
)
);
await
tester
.
tap
(
find
.
byTooltip
(
'Next month'
));
await
tester
.
pumpAndSettle
();
expect
(
_selectedDate
,
equals
(
new
DateTime
(
2016
,
DateTime
.
JULY
,
1
)));
await
tester
.
tapAt
(
const
Offset
(
300.0
,
100.0
));
await
tester
.
tap
(
find
.
text
(
'5'
));
await
tester
.
pumpAndSettle
();
expect
(
_selectedDate
,
equals
(
new
DateTime
(
2016
,
DateTime
.
AUGUST
,
5
)));
await
tester
.
pump
(
const
Duration
(
seconds:
2
));
await
tester
.
drag
(
find
.
byKey
(
_datePickerKey
),
const
Offset
(-
3
00.0
,
0.0
));
await
tester
.
pumpAndSettle
(
const
Duration
(
milliseconds:
100
)
);
await
tester
.
drag
(
find
.
byKey
(
_datePickerKey
),
const
Offset
(-
4
00.0
,
0.0
));
await
tester
.
pumpAndSettle
();
expect
(
_selectedDate
,
equals
(
new
DateTime
(
2016
,
DateTime
.
AUGUST
,
5
)));
await
tester
.
tapAt
(
const
Offset
(
45.0
,
270.0
));
await
tester
.
tap
(
find
.
text
(
'25'
));
await
tester
.
pumpAndSettle
();
expect
(
_selectedDate
,
equals
(
new
DateTime
(
2016
,
DateTime
.
SEPTEMBER
,
25
)));
await
tester
.
pump
(
const
Duration
(
seconds:
2
));
await
tester
.
drag
(
find
.
byKey
(
_datePickerKey
),
const
Offset
(
3
00.0
,
0.0
));
await
tester
.
pumpAndSettle
(
const
Duration
(
milliseconds:
100
)
);
await
tester
.
drag
(
find
.
byKey
(
_datePickerKey
),
const
Offset
(
8
00.0
,
0.0
));
await
tester
.
pumpAndSettle
();
expect
(
_selectedDate
,
equals
(
new
DateTime
(
2016
,
DateTime
.
SEPTEMBER
,
25
)));
await
tester
.
tapAt
(
const
Offset
(
210.0
,
180.0
));
await
tester
.
tap
(
find
.
text
(
'17'
));
await
tester
.
pumpAndSettle
();
expect
(
_selectedDate
,
equals
(
new
DateTime
(
2016
,
DateTime
.
AUGUST
,
17
)));
});
testWidgets
(
'render picker with intrinsic dimensions'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
new
Overlay
(
initialEntries:
<
OverlayEntry
>[
new
OverlayEntry
(
builder:
(
BuildContext
context
)
=>
new
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
return
new
IntrinsicWidth
(
child:
new
IntrinsicHeight
(
child:
new
Material
(
child:
new
SingleChildScrollView
(
child:
new
MonthPicker
(
firstDate:
new
DateTime
(
0
),
lastDate:
new
DateTime
(
9999
),
onChanged:
(
DateTime
value
)
{
},
selectedDate:
new
DateTime
(
2000
,
DateTime
.
JANUARY
,
1
),
),
),
new
MaterialApp
(
home:
new
StatefulBuilder
(
builder:
(
BuildContext
context
,
StateSetter
setState
)
{
return
new
IntrinsicWidth
(
child:
new
IntrinsicHeight
(
child:
new
Material
(
child:
new
SingleChildScrollView
(
child:
new
MonthPicker
(
firstDate:
new
DateTime
(
0
),
lastDate:
new
DateTime
(
9999
),
onChanged:
(
DateTime
value
)
{
},
selectedDate:
new
DateTime
(
2000
,
DateTime
.
JANUARY
,
1
),
),
),
)
;
}
,
)
,
)
,
]
,
)
,
)
,
)
;
}
,
)
,
),
);
await
tester
.
pump
(
const
Duration
(
seconds:
5
));
...
...
packages/flutter/test/widgets/locale_query_test.dart
deleted
100644 → 0
View file @
7676e511
// 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'
;
class
TestLocaleQueryData
extends
LocaleQueryData
{
@override
String
toString
()
=>
'Test data'
;
}
void
main
(
)
{
testWidgets
(
'LocaleQuery control test'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
new
Container
());
expect
(
LocaleQuery
.
of
(
tester
.
element
(
find
.
byType
(
Container
))),
isNull
);
final
LocaleQueryData
data
=
new
TestLocaleQueryData
();
final
Widget
widget
=
new
LocaleQuery
(
data:
data
,
child:
new
Container
(),
);
expect
(
widget
,
hasOneLineDescription
);
expect
(
widget
.
toString
(),
contains
(
'Test data'
));
await
tester
.
pumpWidget
(
widget
);
expect
(
LocaleQuery
.
of
(
tester
.
element
(
find
.
byType
(
Container
))),
equals
(
data
));
});
}
packages/flutter/test/widgets/localizations_test.dart
0 → 100644
View file @
8c2c5022
// Copyright 2017 The Chromium Authors. All rights reserved.rint
// 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/foundation.dart'
;
import
'package:flutter/widgets.dart'
;
class
TestLocalizations
{
TestLocalizations
(
this
.
locale
,
this
.
prefix
);
final
Locale
locale
;
final
String
prefix
;
static
Future
<
TestLocalizations
>
loadSync
(
Locale
locale
,
String
prefix
)
{
return
new
SynchronousFuture
<
TestLocalizations
>(
new
TestLocalizations
(
locale
,
prefix
));
}
static
Future
<
TestLocalizations
>
loadAsync
(
Locale
locale
,
String
prefix
)
{
return
new
Future
<
TestLocalizations
>.
delayed
(
const
Duration
(
milliseconds:
100
))
.
then
((
_
)
=>
new
TestLocalizations
(
locale
,
prefix
));
}
static
TestLocalizations
of
(
BuildContext
context
)
{
return
Localizations
.
of
<
TestLocalizations
>(
context
,
TestLocalizations
);
}
String
get
message
=>
'
${prefix ?? ""}$locale
'
;
}
class
SyncTestLocalizationsDelegate
extends
LocalizationsDelegate
<
TestLocalizations
>
{
SyncTestLocalizationsDelegate
([
this
.
prefix
]);
final
String
prefix
;
// Changing this value triggers a rebuild
final
List
<
bool
>
shouldReloadValues
=
<
bool
>[];
@override
Future
<
TestLocalizations
>
load
(
Locale
locale
)
=>
TestLocalizations
.
loadSync
(
locale
,
prefix
);
@override
bool
shouldReload
(
SyncTestLocalizationsDelegate
old
)
{
shouldReloadValues
.
add
(
prefix
!=
old
.
prefix
);
return
prefix
!=
old
.
prefix
;
}
}
class
AsyncTestLocalizationsDelegate
extends
LocalizationsDelegate
<
TestLocalizations
>
{
AsyncTestLocalizationsDelegate
([
this
.
prefix
]);
final
String
prefix
;
// Changing this value triggers a rebuild
final
List
<
bool
>
shouldReloadValues
=
<
bool
>[];
@override
Future
<
TestLocalizations
>
load
(
Locale
locale
)
=>
TestLocalizations
.
loadAsync
(
locale
,
prefix
);
@override
bool
shouldReload
(
AsyncTestLocalizationsDelegate
old
)
{
shouldReloadValues
.
add
(
prefix
!=
old
.
prefix
);
return
prefix
!=
old
.
prefix
;
}
}
class
MoreLocalizations
{
MoreLocalizations
(
this
.
locale
);
final
Locale
locale
;
static
Future
<
MoreLocalizations
>
loadSync
(
Locale
locale
)
{
return
new
SynchronousFuture
<
MoreLocalizations
>(
new
MoreLocalizations
(
locale
));
}
static
Future
<
MoreLocalizations
>
loadAsync
(
Locale
locale
)
{
return
new
Future
<
MoreLocalizations
>.
delayed
(
const
Duration
(
milliseconds:
100
))
.
then
((
_
)
=>
new
MoreLocalizations
(
locale
));
}
static
MoreLocalizations
of
(
BuildContext
context
)
{
return
Localizations
.
of
<
MoreLocalizations
>(
context
,
MoreLocalizations
);
}
String
get
message
=>
'
$locale
'
;
}
class
SyncMoreLocalizationsDelegate
extends
LocalizationsDelegate
<
MoreLocalizations
>
{
@override
Future
<
MoreLocalizations
>
load
(
Locale
locale
)
=>
MoreLocalizations
.
loadSync
(
locale
);
@override
bool
shouldReload
(
SyncMoreLocalizationsDelegate
old
)
=>
false
;
}
class
AsyncMoreLocalizationsDelegate
extends
LocalizationsDelegate
<
MoreLocalizations
>
{
@override
Future
<
MoreLocalizations
>
load
(
Locale
locale
)
=>
MoreLocalizations
.
loadAsync
(
locale
);
@override
bool
shouldReload
(
AsyncMoreLocalizationsDelegate
old
)
=>
false
;
}
Widget
buildFrame
(
{
Locale
locale
,
Iterable
<
LocalizationsDelegate
<
dynamic
>>
delegates
,
WidgetBuilder
buildContent
,
})
{
return
new
WidgetsApp
(
color:
const
Color
(
0xFFFFFFFF
),
locale:
locale
,
localizationsDelegates:
delegates
,
onGenerateRoute:
(
RouteSettings
settings
)
{
return
new
PageRouteBuilder
<
Null
>(
pageBuilder:
(
BuildContext
context
,
Animation
<
double
>
_
,
Animation
<
double
>
__
)
{
return
buildContent
(
context
);
}
);
},
);
}
void
main
(
)
{
testWidgets
(
'Localizations.localeFor in a WidgetsApp with system locale'
,
(
WidgetTester
tester
)
async
{
BuildContext
pageContext
;
await
tester
.
pumpWidget
(
buildFrame
(
buildContent:
(
BuildContext
context
)
{
pageContext
=
context
;
return
new
Text
(
'Hello World'
);
}
)
);
await
tester
.
binding
.
setLocale
(
'en'
,
'GB'
);
await
tester
.
pump
();
expect
(
Localizations
.
localeOf
(
pageContext
),
const
Locale
(
'en'
,
'GB'
));
await
tester
.
binding
.
setLocale
(
'en'
,
'US'
);
await
tester
.
pump
();
expect
(
Localizations
.
localeOf
(
pageContext
),
const
Locale
(
'en'
,
'US'
));
});
testWidgets
(
'Localizations.localeFor in a WidgetsApp with an explicit locale'
,
(
WidgetTester
tester
)
async
{
final
Locale
locale
=
const
Locale
(
'en'
,
'US'
);
BuildContext
pageContext
;
await
tester
.
pumpWidget
(
buildFrame
(
locale:
locale
,
buildContent:
(
BuildContext
context
)
{
pageContext
=
context
;
return
new
Text
(
'Hello World'
);
},
)
);
expect
(
Localizations
.
localeOf
(
pageContext
),
locale
);
await
tester
.
binding
.
setLocale
(
'en'
,
'GB'
);
await
tester
.
pump
();
// The WidgetApp's explicit locale overrides the system's locale.
expect
(
Localizations
.
localeOf
(
pageContext
),
locale
);
});
testWidgets
(
'Synchronously loaded localizations in a WidgetsApp'
,
(
WidgetTester
tester
)
async
{
BuildContext
pageContext
;
await
tester
.
pumpWidget
(
buildFrame
(
delegates:
<
LocalizationsDelegate
<
dynamic
>>[
new
SyncTestLocalizationsDelegate
()
],
buildContent:
(
BuildContext
context
)
{
pageContext
=
context
;
return
new
Text
(
TestLocalizations
.
of
(
context
).
message
);
}
)
);
expect
(
TestLocalizations
.
of
(
pageContext
),
isNotNull
);
expect
(
find
.
text
(
'_'
),
findsOneWidget
);
// default test locale is '_'
await
tester
.
binding
.
setLocale
(
'en'
,
'GB'
);
await
tester
.
pump
();
expect
(
find
.
text
(
'en_GB'
),
findsOneWidget
);
await
tester
.
binding
.
setLocale
(
'en'
,
'US'
);
await
tester
.
pump
();
expect
(
find
.
text
(
'en_US'
),
findsOneWidget
);
});
testWidgets
(
'Asynchronously loaded localizations in a WidgetsApp'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
buildFrame
(
delegates:
<
LocalizationsDelegate
<
dynamic
>>[
new
AsyncTestLocalizationsDelegate
(),
],
buildContent:
(
BuildContext
context
)
{
return
new
Text
(
TestLocalizations
.
of
(
context
).
message
);
}
)
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
50
));
// TestLocalizations.loadAsync() takes 100ms
expect
(
find
.
text
(
'_'
),
findsNothing
);
// TestLocalizations hasn't been loaded yet
await
tester
.
pump
(
const
Duration
(
milliseconds:
50
));
// TestLocalizations.loadAsync() completes
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'_'
),
findsOneWidget
);
// default test locale is '_'
await
tester
.
binding
.
setLocale
(
'en'
,
'US'
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
100
));
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'en_US'
),
findsOneWidget
);
await
tester
.
binding
.
setLocale
(
'en'
,
'GB'
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
50
));
// TestLocalizations.loadAsync() hasn't completed yet so the old text
// localization is still displayed
expect
(
find
.
text
(
'en_US'
),
findsOneWidget
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
50
));
// finish the async load
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'en_GB'
),
findsOneWidget
);
});
testWidgets
(
'Localizations with multiple sync delegates'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
buildFrame
(
delegates:
<
LocalizationsDelegate
<
dynamic
>>[
new
SyncTestLocalizationsDelegate
(),
new
SyncMoreLocalizationsDelegate
(),
],
locale:
const
Locale
(
'en'
,
'US'
),
buildContent:
(
BuildContext
context
)
{
return
new
Column
(
children:
<
Widget
>[
new
Text
(
'A:
${TestLocalizations.of(context).message}
'
),
new
Text
(
'B:
${MoreLocalizations.of(context).message}
'
),
],
);
}
)
);
// All localizations were loaded synchonously
expect
(
find
.
text
(
'A: en_US'
),
findsOneWidget
);
expect
(
find
.
text
(
'B: en_US'
),
findsOneWidget
);
});
testWidgets
(
'Localizations with multiple delegates'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
buildFrame
(
delegates:
<
LocalizationsDelegate
<
dynamic
>>[
new
SyncTestLocalizationsDelegate
(),
new
AsyncMoreLocalizationsDelegate
(),
// No resources until this completes
],
locale:
const
Locale
(
'en'
,
'US'
),
buildContent:
(
BuildContext
context
)
{
return
new
Column
(
children:
<
Widget
>[
new
Text
(
'A:
${TestLocalizations.of(context).message}
'
),
new
Text
(
'B:
${MoreLocalizations.of(context).message}
'
),
],
);
}
)
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
50
));
expect
(
find
.
text
(
'A: en_US'
),
findsNothing
);
// MoreLocalizations.load() hasn't completed yet
expect
(
find
.
text
(
'B: en_US'
),
findsNothing
);
await
tester
.
pump
(
const
Duration
(
milliseconds:
50
));
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'A: en_US'
),
findsOneWidget
);
expect
(
find
.
text
(
'B: en_US'
),
findsOneWidget
);
});
testWidgets
(
'Muliple Localizations'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
buildFrame
(
delegates:
<
LocalizationsDelegate
<
dynamic
>>[
new
SyncTestLocalizationsDelegate
(),
],
locale:
const
Locale
(
'en'
,
'US'
),
buildContent:
(
BuildContext
context
)
{
return
new
Column
(
children:
<
Widget
>[
new
Text
(
'A:
${TestLocalizations.of(context).message}
'
),
new
Localizations
(
locale:
const
Locale
(
'en'
,
'GB'
),
delegates:
<
LocalizationsDelegate
<
dynamic
>>[
new
SyncTestLocalizationsDelegate
(),
],
// Create a new context within the en_GB Localization
child:
new
Builder
(
builder:
(
BuildContext
context
)
{
return
new
Text
(
'B:
${TestLocalizations.of(context).message}
'
);
},
),
),
],
);
}
)
);
expect
(
find
.
text
(
'A: en_US'
),
findsOneWidget
);
expect
(
find
.
text
(
'B: en_GB'
),
findsOneWidget
);
});
// If both the locale and the length and type of a Localizations delegate list
// stays the same BUT one of its delegate.shouldReload() methods returns true,
// then the dependent widgets should rebuild.
testWidgets
(
'Localizations sync delegate shouldReload returns true'
,
(
WidgetTester
tester
)
async
{
final
SyncTestLocalizationsDelegate
originalDelegate
=
new
SyncTestLocalizationsDelegate
();
await
tester
.
pumpWidget
(
buildFrame
(
delegates:
<
LocalizationsDelegate
<
dynamic
>>[
originalDelegate
,
new
SyncMoreLocalizationsDelegate
(),
],
locale:
const
Locale
(
'en'
,
'US'
),
buildContent:
(
BuildContext
context
)
{
return
new
Column
(
children:
<
Widget
>[
new
Text
(
'A:
${TestLocalizations.of(context).message}
'
),
new
Text
(
'B:
${MoreLocalizations.of(context).message}
'
),
],
);
}
)
);
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'A: en_US'
),
findsOneWidget
);
expect
(
find
.
text
(
'B: en_US'
),
findsOneWidget
);
expect
(
originalDelegate
.
shouldReloadValues
,
<
bool
>[]);
final
SyncTestLocalizationsDelegate
modifiedDelegate
=
new
SyncTestLocalizationsDelegate
(
'---'
);
await
tester
.
pumpWidget
(
buildFrame
(
delegates:
<
LocalizationsDelegate
<
dynamic
>>[
modifiedDelegate
,
new
SyncMoreLocalizationsDelegate
(),
],
locale:
const
Locale
(
'en'
,
'US'
),
buildContent:
(
BuildContext
context
)
{
return
new
Column
(
children:
<
Widget
>[
new
Text
(
'A:
${TestLocalizations.of(context).message}
'
),
new
Text
(
'B:
${MoreLocalizations.of(context).message}
'
),
],
);
}
)
);
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'A: ---en_US'
),
findsOneWidget
);
expect
(
find
.
text
(
'B: en_US'
),
findsOneWidget
);
expect
(
modifiedDelegate
.
shouldReloadValues
,
<
bool
>[
true
]);
});
testWidgets
(
'Localizations async delegate shouldReload returns true'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
buildFrame
(
delegates:
<
LocalizationsDelegate
<
dynamic
>>[
new
AsyncTestLocalizationsDelegate
(),
new
AsyncMoreLocalizationsDelegate
(),
],
locale:
const
Locale
(
'en'
,
'US'
),
buildContent:
(
BuildContext
context
)
{
return
new
Column
(
children:
<
Widget
>[
new
Text
(
'A:
${TestLocalizations.of(context).message}
'
),
new
Text
(
'B:
${MoreLocalizations.of(context).message}
'
),
],
);
}
)
);
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'A: en_US'
),
findsOneWidget
);
expect
(
find
.
text
(
'B: en_US'
),
findsOneWidget
);
final
AsyncTestLocalizationsDelegate
modifiedDelegate
=
new
AsyncTestLocalizationsDelegate
(
'---'
);
await
tester
.
pumpWidget
(
buildFrame
(
delegates:
<
LocalizationsDelegate
<
dynamic
>>[
modifiedDelegate
,
new
AsyncMoreLocalizationsDelegate
(),
],
locale:
const
Locale
(
'en'
,
'US'
),
buildContent:
(
BuildContext
context
)
{
return
new
Column
(
children:
<
Widget
>[
new
Text
(
'A:
${TestLocalizations.of(context).message}
'
),
new
Text
(
'B:
${MoreLocalizations.of(context).message}
'
),
],
);
}
)
);
await
tester
.
pumpAndSettle
();
expect
(
find
.
text
(
'A: ---en_US'
),
findsOneWidget
);
expect
(
find
.
text
(
'B: en_US'
),
findsOneWidget
);
expect
(
modifiedDelegate
.
shouldReloadValues
,
<
bool
>[
true
]);
});
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment