1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/// @docImport 'package:flutter/material.dart';
6/// @docImport 'package:flutter_localizations/flutter_localizations.dart';
7///
8/// @docImport 'app.dart';
9/// @docImport 'reorderable_list.dart';
10library;
11
12import 'package:flutter/foundation.dart';
13import 'package:flutter/rendering.dart';
14
15import 'basic.dart';
16import 'debug.dart';
17import 'framework.dart';
18
19// Examples can assume:
20// class Intl { Intl._(); static String message(String s, { String? name, String? locale }) => ''; }
21// Future initializeMessages(String locale) => Future.value();
22// late BuildContext context;
23// class Foo { }
24// const Widget myWidget = Placeholder();
25
26// Used by loadAll() to record LocalizationsDelegate.load() futures we're
27// waiting for.
28class _Pending {
29 _Pending(this.delegate, this.futureValue);
30 final LocalizationsDelegate<dynamic> delegate;
31 final Future<dynamic> futureValue;
32}
33
34// A utility function used by Localizations to generate one future
35// that completes when all of the LocalizationsDelegate.load() futures
36// complete. The returned map is indexed by each delegate's type.
37//
38// The input future values must have distinct types.
39//
40// The returned Future will resolve when all of the input map's
41// future values have resolved. If all of the input map's values are
42// SynchronousFutures then a SynchronousFuture will be returned
43// immediately.
44//
45// This is more complicated than just applying Future.wait to input
46// because some of the input.values may be SynchronousFutures. We don't want
47// to Future.wait for the synchronous futures.
48Future<Map<Type, dynamic>> _loadAll(
49 Locale locale,
50 Iterable<LocalizationsDelegate<dynamic>> allDelegates,
51) {
52 final Map<Type, dynamic> output = <Type, dynamic>{};
53 List<_Pending>? pendingList;
54
55 // Only load the first delegate for each delegate type that supports
56 // locale.languageCode.
57 final Set<Type> types = <Type>{};
58 final List<LocalizationsDelegate<dynamic>> delegates = <LocalizationsDelegate<dynamic>>[];
59 for (final LocalizationsDelegate<dynamic> delegate in allDelegates) {
60 if (!types.contains(delegate.type) && delegate.isSupported(locale)) {
61 types.add(delegate.type);
62 delegates.add(delegate);
63 }
64 }
65
66 for (final LocalizationsDelegate<dynamic> delegate in delegates) {
67 final Future<dynamic> inputValue = delegate.load(locale);
68 dynamic completedValue;
69 final Future<dynamic> futureValue = inputValue.then<dynamic>((dynamic value) {
70 return completedValue = value;
71 });
72 if (completedValue != null) {
73 // inputValue was a SynchronousFuture
74 final Type type = delegate.type;
75 assert(!output.containsKey(type));
76 output[type] = completedValue;
77 } else {
78 pendingList ??= <_Pending>[];
79 pendingList.add(_Pending(delegate, futureValue));
80 }
81 }
82
83 // All of the delegate.load() values were synchronous futures, we're done.
84 if (pendingList == null) {
85 return SynchronousFuture<Map<Type, dynamic>>(output);
86 }
87
88 // Some of delegate.load() values were asynchronous futures. Wait for them.
89 return Future.wait<dynamic>(
90 pendingList.map<Future<dynamic>>((_Pending p) => p.futureValue),
91 ).then<Map<Type, dynamic>>((List<dynamic> values) {
92 assert(values.length == pendingList!.length);
93 for (int i = 0; i < values.length; i += 1) {
94 final Type type = pendingList![i].delegate.type;
95 assert(!output.containsKey(type));
96 output[type] = values[i];
97 }
98 return output;
99 });
100}
101
102/// A factory for a set of localized resources of type `T`, to be loaded by a
103/// [Localizations] widget.
104///
105/// Typical applications have one [Localizations] widget which is created by the
106/// [WidgetsApp] and configured with the app's `localizationsDelegates`
107/// parameter (a list of delegates). The delegate's [type] is used to identify
108/// the object created by an individual delegate's [load] method.
109///
110/// An example of a class used as the value of `T` here would be
111/// [MaterialLocalizations].
112abstract class LocalizationsDelegate<T> {
113 /// Abstract const constructor. This constructor enables subclasses to provide
114 /// const constructors so that they can be used in const expressions.
115 const LocalizationsDelegate();
116
117 /// Whether resources for the given locale can be loaded by this delegate.
118 ///
119 /// Return true if the instance of `T` loaded by this delegate's [load]
120 /// method supports the given `locale`'s language.
121 bool isSupported(Locale locale);
122
123 /// Start loading the resources for `locale`. The returned future completes
124 /// when the resources have finished loading.
125 ///
126 /// It's assumed that this method will return an object that contains a
127 /// collection of related string resources (typically defined with one method
128 /// per resource). The object will be retrieved with [Localizations.of].
129 Future<T> load(Locale locale);
130
131 /// Returns true if the resources for this delegate should be loaded
132 /// again by calling the [load] method.
133 ///
134 /// This method is called whenever its [Localizations] widget is
135 /// rebuilt. If it returns true then dependent widgets will be rebuilt
136 /// after [load] has completed.
137 bool shouldReload(covariant LocalizationsDelegate<T> old);
138
139 /// The type of the object returned by the [load] method, T by default.
140 ///
141 /// This type is used to retrieve the object "loaded" by this
142 /// [LocalizationsDelegate] from the [Localizations] inherited widget.
143 /// For example the object loaded by `LocalizationsDelegate<Foo>` would
144 /// be retrieved with:
145 ///
146 /// ```dart
147 /// Foo foo = Localizations.of<Foo>(context, Foo)!;
148 /// ```
149 ///
150 /// It's rarely necessary to override this getter.
151 Type get type => T;
152
153 @override
154 String toString() => '${objectRuntimeType(this, 'LocalizationsDelegate')}[$type]';
155}
156
157/// Interface for localized resource values for the lowest levels of the Flutter
158/// framework.
159///
160/// This class also maps locales to a specific [Directionality] using the
161/// [textDirection] property.
162///
163/// See also:
164///
165/// * [DefaultWidgetsLocalizations], which implements this interface and
166/// supports a variety of locales.
167abstract class WidgetsLocalizations {
168 /// The reading direction for text in this locale.
169 TextDirection get textDirection;
170
171 /// The semantics label used for [SliverReorderableList] to reorder an item in the
172 /// list to the start of the list.
173 String get reorderItemToStart;
174
175 /// The semantics label used for [SliverReorderableList] to reorder an item in the
176 /// list to the end of the list.
177 String get reorderItemToEnd;
178
179 /// The semantics label used for [SliverReorderableList] to reorder an item in the
180 /// list one space up the list.
181 String get reorderItemUp;
182
183 /// The semantics label used for [SliverReorderableList] to reorder an item in the
184 /// list one space down the list.
185 String get reorderItemDown;
186
187 /// The semantics label used for [SliverReorderableList] to reorder an item in the
188 /// list one space left in the list.
189 String get reorderItemLeft;
190
191 /// The semantics label used for [SliverReorderableList] to reorder an item in the
192 /// list one space right in the list.
193 String get reorderItemRight;
194
195 /// Label for "copy" edit buttons and menu items.
196 String get copyButtonLabel;
197
198 /// Label for "cut" edit buttons and menu items.
199 String get cutButtonLabel;
200
201 /// Label for "paste" edit buttons and menu items.
202 String get pasteButtonLabel;
203
204 /// Label for "select all" edit buttons and menu items.
205 String get selectAllButtonLabel;
206
207 /// Label for "look up" edit buttons and menu items.
208 String get lookUpButtonLabel;
209
210 /// Label for "search web" edit buttons and menu items.
211 String get searchWebButtonLabel;
212
213 /// Label for "share" edit buttons and menu items.
214 String get shareButtonLabel;
215
216 /// The `WidgetsLocalizations` from the closest [Localizations] instance
217 /// that encloses the given context.
218 ///
219 /// This method is just a convenient shorthand for:
220 /// `Localizations.of<WidgetsLocalizations>(context, WidgetsLocalizations)!`.
221 ///
222 /// References to the localized resources defined by this class are typically
223 /// written in terms of this method. For example:
224 ///
225 /// ```dart
226 /// textDirection: WidgetsLocalizations.of(context).textDirection,
227 /// ```
228 static WidgetsLocalizations of(BuildContext context) {
229 assert(debugCheckHasWidgetsLocalizations(context));
230 return Localizations.of<WidgetsLocalizations>(context, WidgetsLocalizations)!;
231 }
232}
233
234class _WidgetsLocalizationsDelegate extends LocalizationsDelegate<WidgetsLocalizations> {
235 const _WidgetsLocalizationsDelegate();
236
237 // This is convenient simplification. It would be more correct test if the locale's
238 // text-direction is LTR.
239 @override
240 bool isSupported(Locale locale) => true;
241
242 @override
243 Future<WidgetsLocalizations> load(Locale locale) => DefaultWidgetsLocalizations.load(locale);
244
245 @override
246 bool shouldReload(_WidgetsLocalizationsDelegate old) => false;
247
248 @override
249 String toString() => 'DefaultWidgetsLocalizations.delegate(en_US)';
250}
251
252/// US English localizations for the widgets library.
253///
254/// See also:
255///
256/// * [GlobalWidgetsLocalizations], which provides widgets localizations for
257/// many languages.
258/// * [WidgetsApp.localizationsDelegates], which automatically includes
259/// [DefaultWidgetsLocalizations.delegate] by default.
260class DefaultWidgetsLocalizations implements WidgetsLocalizations {
261 /// Construct an object that defines the localized values for the widgets
262 /// library for US English (only).
263 ///
264 /// [LocalizationsDelegate] implementations typically call the static [load]
265 const DefaultWidgetsLocalizations();
266
267 @override
268 String get reorderItemUp => 'Move up';
269
270 @override
271 String get reorderItemDown => 'Move down';
272
273 @override
274 String get reorderItemLeft => 'Move left';
275
276 @override
277 String get reorderItemRight => 'Move right';
278
279 @override
280 String get reorderItemToEnd => 'Move to the end';
281
282 @override
283 String get reorderItemToStart => 'Move to the start';
284
285 @override
286 String get copyButtonLabel => 'Copy';
287
288 @override
289 String get cutButtonLabel => 'Cut';
290
291 @override
292 String get pasteButtonLabel => 'Paste';
293
294 @override
295 String get selectAllButtonLabel => 'Select all';
296
297 @override
298 String get lookUpButtonLabel => 'Look Up';
299
300 @override
301 String get searchWebButtonLabel => 'Search Web';
302
303 @override
304 String get shareButtonLabel => 'Share';
305
306 @override
307 TextDirection get textDirection => TextDirection.ltr;
308
309 /// Creates an object that provides US English resource values for the
310 /// lowest levels of the widgets library.
311 ///
312 /// The [locale] parameter is ignored.
313 ///
314 /// This method is typically used to create a [LocalizationsDelegate].
315 /// The [WidgetsApp] does so by default.
316 static Future<WidgetsLocalizations> load(Locale locale) {
317 return SynchronousFuture<WidgetsLocalizations>(const DefaultWidgetsLocalizations());
318 }
319
320 /// A [LocalizationsDelegate] that uses [DefaultWidgetsLocalizations.load]
321 /// to create an instance of this class.
322 ///
323 /// [WidgetsApp] automatically adds this value to [WidgetsApp.localizationsDelegates].
324 static const LocalizationsDelegate<WidgetsLocalizations> delegate =
325 _WidgetsLocalizationsDelegate();
326}
327
328class _LocalizationsScope extends InheritedWidget {
329 const _LocalizationsScope({
330 super.key,
331 required this.locale,
332 required this.localizationsState,
333 required this.typeToResources,
334 required super.child,
335 });
336
337 final Locale locale;
338 final _LocalizationsState localizationsState;
339 final Map<Type, dynamic> typeToResources;
340
341 @override
342 bool updateShouldNotify(_LocalizationsScope old) {
343 return typeToResources != old.typeToResources;
344 }
345}
346
347/// Defines the [Locale] for its `child` and the localized resources that the
348/// child depends on.
349///
350/// ## Defining localized resources
351///
352/// {@tool snippet}
353///
354/// This following class is defined in terms of the
355/// [Dart `intl` package](https://github.com/dart-lang/intl). Using the `intl`
356/// package isn't required.
357///
358/// ```dart
359/// class MyLocalizations {
360/// MyLocalizations(this.locale);
361///
362/// final Locale locale;
363///
364/// static Future<MyLocalizations> load(Locale locale) {
365/// return initializeMessages(locale.toString())
366/// .then((void _) {
367/// return MyLocalizations(locale);
368/// });
369/// }
370///
371/// static MyLocalizations of(BuildContext context) {
372/// return Localizations.of<MyLocalizations>(context, MyLocalizations)!;
373/// }
374///
375/// String title() => Intl.message('<title>', name: 'title', locale: locale.toString());
376/// // ... more Intl.message() methods like title()
377/// }
378/// ```
379/// {@end-tool}
380/// A class based on the `intl` package imports a generated message catalog that provides
381/// the `initializeMessages()` function and the per-locale backing store for `Intl.message()`.
382/// The message catalog is produced by an `intl` tool that analyzes the source code for
383/// classes that contain `Intl.message()` calls. In this case that would just be the
384/// `MyLocalizations` class.
385///
386/// One could choose another approach for loading localized resources and looking them up while
387/// still conforming to the structure of this example.
388///
389/// ## Loading localized resources
390///
391/// Localized resources are loaded by the list of [LocalizationsDelegate]
392/// `delegates`. Each delegate is essentially a factory for a collection
393/// of localized resources. There are multiple delegates because there are
394/// multiple sources for localizations within an app.
395///
396/// Delegates are typically simple subclasses of [LocalizationsDelegate] that
397/// override [LocalizationsDelegate.load]. For example a delegate for the
398/// `MyLocalizations` class defined above would be:
399///
400/// ```dart
401/// // continuing from previous example...
402/// class _MyDelegate extends LocalizationsDelegate<MyLocalizations> {
403/// @override
404/// Future<MyLocalizations> load(Locale locale) => MyLocalizations.load(locale);
405///
406/// @override
407/// bool isSupported(Locale locale) {
408/// // in a real implementation this would only return true for
409/// // locales that are definitely supported.
410/// return true;
411/// }
412///
413/// @override
414/// bool shouldReload(_MyDelegate old) => false;
415/// }
416/// ```
417///
418/// Each delegate can be viewed as a factory for objects that encapsulate a set
419/// of localized resources. These objects are retrieved with
420/// by runtime type with [Localizations.of].
421///
422/// The [WidgetsApp] class creates a [Localizations] widget so most apps
423/// will not need to create one. The widget app's [Localizations] delegates can
424/// be initialized with [WidgetsApp.localizationsDelegates]. The [MaterialApp]
425/// class also provides a `localizationsDelegates` parameter that's just
426/// passed along to the [WidgetsApp].
427///
428/// ## Obtaining localized resources for use in user interfaces
429///
430/// Apps should retrieve collections of localized resources with
431/// `Localizations.of<MyLocalizations>(context, MyLocalizations)`,
432/// where MyLocalizations is an app specific class defines one function per
433/// resource. This is conventionally done by a static `.of` method on the
434/// custom localized resource class (`MyLocalizations` in the example above).
435///
436/// For example, using the `MyLocalizations` class defined above, one would
437/// lookup a localized title string like this:
438///
439/// ```dart
440/// // continuing from previous example...
441/// MyLocalizations.of(context).title()
442/// ```
443///
444/// If [Localizations] were to be rebuilt with a new `locale` then
445/// the widget subtree that corresponds to [BuildContext] `context` would
446/// be rebuilt after the corresponding resources had been loaded.
447///
448/// This class is effectively an [InheritedWidget]. If it's rebuilt with
449/// a new `locale` or a different list of delegates or any of its
450/// delegates' [LocalizationsDelegate.shouldReload()] methods returns true,
451/// then widgets that have created a dependency by calling
452/// `Localizations.of(context)` will be rebuilt after the resources
453/// for the new locale have been loaded.
454///
455/// The [Localizations] widget also instantiates [Directionality] in order to
456/// support the appropriate [Directionality.textDirection] of the localized
457/// resources.
458class Localizations extends StatefulWidget {
459 /// Create a widget from which localizations (like translated strings) can be obtained.
460 Localizations({super.key, required this.locale, required this.delegates, this.child})
461 : assert(
462 delegates.any(
463 (LocalizationsDelegate<dynamic> delegate) =>
464 delegate is LocalizationsDelegate<WidgetsLocalizations>,
465 ),
466 );
467
468 /// Overrides the inherited [Locale] or [LocalizationsDelegate]s for `child`.
469 ///
470 /// This factory constructor is used for the (usually rare) situation where part
471 /// of an app should be localized for a different locale than the one defined
472 /// for the device, or if its localizations should come from a different list
473 /// of [LocalizationsDelegate]s than the list defined by
474 /// [WidgetsApp.localizationsDelegates].
475 ///
476 /// For example you could specify that `myWidget` was only to be localized for
477 /// the US English locale:
478 ///
479 /// ```dart
480 /// Widget build(BuildContext context) {
481 /// return Localizations.override(
482 /// context: context,
483 /// locale: const Locale('en', 'US'),
484 /// child: myWidget,
485 /// );
486 /// }
487 /// ```
488 ///
489 /// The `locale` and `delegates` parameters default to the [Localizations.locale]
490 /// and [Localizations.delegates] values from the nearest [Localizations] ancestor.
491 ///
492 /// To override the [Localizations.locale] or [Localizations.delegates] for an
493 /// entire app, specify [WidgetsApp.locale] or [WidgetsApp.localizationsDelegates]
494 /// (or specify the same parameters for [MaterialApp]).
495 factory Localizations.override({
496 Key? key,
497 required BuildContext context,
498 Locale? locale,
499 List<LocalizationsDelegate<dynamic>>? delegates,
500 Widget? child,
501 }) {
502 final List<LocalizationsDelegate<dynamic>> mergedDelegates = Localizations._delegatesOf(
503 context,
504 );
505 if (delegates != null) {
506 mergedDelegates.insertAll(0, delegates);
507 }
508 return Localizations(
509 key: key,
510 locale: locale ?? Localizations.localeOf(context),
511 delegates: mergedDelegates,
512 child: child,
513 );
514 }
515
516 /// The resources returned by [Localizations.of] will be specific to this locale.
517 final Locale locale;
518
519 /// This list collectively defines the localized resources objects that can
520 /// be retrieved with [Localizations.of].
521 final List<LocalizationsDelegate<dynamic>> delegates;
522
523 /// The widget below this widget in the tree.
524 ///
525 /// {@macro flutter.widgets.ProxyWidget.child}
526 final Widget? child;
527
528 /// The locale of the Localizations widget for the widget tree that
529 /// corresponds to [BuildContext] `context`.
530 ///
531 /// If no [Localizations] widget is in scope then the [Localizations.localeOf]
532 /// method will throw an exception.
533 static Locale localeOf(BuildContext context) {
534 final _LocalizationsScope? scope =
535 context.dependOnInheritedWidgetOfExactType<_LocalizationsScope>();
536 assert(() {
537 if (scope == null) {
538 throw FlutterError(
539 'Requested the Locale of a context that does not include a Localizations ancestor.\n'
540 'To request the Locale, the context used to retrieve the Localizations widget must '
541 'be that of a widget that is a descendant of a Localizations widget.',
542 );
543 }
544 if (scope.localizationsState.locale == null) {
545 throw FlutterError(
546 'Localizations.localeOf found a Localizations widget that had a unexpected null locale.\n',
547 );
548 }
549 return true;
550 }());
551 return scope!.localizationsState.locale!;
552 }
553
554 /// The locale of the Localizations widget for the widget tree that
555 /// corresponds to [BuildContext] `context`.
556 ///
557 /// If no [Localizations] widget is in scope then this function will return
558 /// null.
559 static Locale? maybeLocaleOf(BuildContext context) {
560 final _LocalizationsScope? scope =
561 context.dependOnInheritedWidgetOfExactType<_LocalizationsScope>();
562 return scope?.localizationsState.locale;
563 }
564
565 // There doesn't appear to be a need to make this public. See the
566 // Localizations.override factory constructor.
567 static List<LocalizationsDelegate<dynamic>> _delegatesOf(BuildContext context) {
568 final _LocalizationsScope? scope =
569 context.dependOnInheritedWidgetOfExactType<_LocalizationsScope>();
570 assert(scope != null, 'a Localizations ancestor was not found');
571 return List<LocalizationsDelegate<dynamic>>.of(scope!.localizationsState.widget.delegates);
572 }
573
574 /// Returns the localized resources object of the given `type` for the widget
575 /// tree that corresponds to the given `context`.
576 ///
577 /// Returns null if no resources object of the given `type` exists within
578 /// the given `context`.
579 ///
580 /// This method is typically used by a static factory method on the `type`
581 /// class. For example Flutter's MaterialLocalizations class looks up Material
582 /// resources with a method defined like this:
583 ///
584 /// ```dart
585 /// static MaterialLocalizations of(BuildContext context) {
586 /// return Localizations.of<MaterialLocalizations>(context, MaterialLocalizations)!;
587 /// }
588 /// ```
589 static T? of<T>(BuildContext context, Type type) {
590 final _LocalizationsScope? scope =
591 context.dependOnInheritedWidgetOfExactType<_LocalizationsScope>();
592 return scope?.localizationsState.resourcesFor<T?>(type);
593 }
594
595 @override
596 State<Localizations> createState() => _LocalizationsState();
597
598 @override
599 void debugFillProperties(DiagnosticPropertiesBuilder properties) {
600 super.debugFillProperties(properties);
601 properties.add(DiagnosticsProperty<Locale>('locale', locale));
602 properties.add(IterableProperty<LocalizationsDelegate<dynamic>>('delegates', delegates));
603 }
604}
605
606class _LocalizationsState extends State<Localizations> {
607 final GlobalKey _localizedResourcesScopeKey = GlobalKey();
608 Map<Type, dynamic> _typeToResources = <Type, dynamic>{};
609
610 Locale? get locale => _locale;
611 Locale? _locale;
612
613 @override
614 void initState() {
615 super.initState();
616 load(widget.locale);
617 }
618
619 bool _anyDelegatesShouldReload(Localizations old) {
620 if (widget.delegates.length != old.delegates.length) {
621 return true;
622 }
623 final List<LocalizationsDelegate<dynamic>> delegates = widget.delegates.toList();
624 final List<LocalizationsDelegate<dynamic>> oldDelegates = old.delegates.toList();
625 for (int i = 0; i < delegates.length; i += 1) {
626 final LocalizationsDelegate<dynamic> delegate = delegates[i];
627 final LocalizationsDelegate<dynamic> oldDelegate = oldDelegates[i];
628 if (delegate.runtimeType != oldDelegate.runtimeType || delegate.shouldReload(oldDelegate)) {
629 return true;
630 }
631 }
632 return false;
633 }
634
635 @override
636 void didUpdateWidget(Localizations old) {
637 super.didUpdateWidget(old);
638 if (widget.locale != old.locale || (_anyDelegatesShouldReload(old))) {
639 load(widget.locale);
640 }
641 }
642
643 void load(Locale locale) {
644 final Iterable<LocalizationsDelegate<dynamic>> delegates = widget.delegates;
645 if (delegates.isEmpty) {
646 _locale = locale;
647 return;
648 }
649
650 Map<Type, dynamic>? typeToResources;
651 final Future<Map<Type, dynamic>> typeToResourcesFuture = _loadAll(
652 locale,
653 delegates,
654 ).then<Map<Type, dynamic>>((Map<Type, dynamic> value) {
655 return typeToResources = value;
656 });
657
658 if (typeToResources != null) {
659 // All of the delegates' resources loaded synchronously.
660 _typeToResources = typeToResources!;
661 _locale = locale;
662 } else {
663 // - Don't rebuild the dependent widgets until the resources for the new locale
664 // have finished loading. Until then the old locale will continue to be used.
665 // - If we're running at app startup time then defer reporting the first
666 // "useful" frame until after the async load has completed.
667 RendererBinding.instance.deferFirstFrame();
668 typeToResourcesFuture.then<void>((Map<Type, dynamic> value) {
669 if (mounted) {
670 setState(() {
671 _typeToResources = value;
672 _locale = locale;
673 });
674 }
675 RendererBinding.instance.allowFirstFrame();
676 });
677 }
678 }
679
680 T resourcesFor<T>(Type type) {
681 final T resources = _typeToResources[type] as T;
682 return resources;
683 }
684
685 TextDirection get _textDirection {
686 final WidgetsLocalizations resources =
687 _typeToResources[WidgetsLocalizations] as WidgetsLocalizations;
688 return resources.textDirection;
689 }
690
691 @override
692 Widget build(BuildContext context) {
693 if (_locale == null) {
694 return const SizedBox.shrink();
695 }
696 return Semantics(
697 textDirection: _textDirection,
698 child: _LocalizationsScope(
699 key: _localizedResourcesScopeKey,
700 locale: _locale!,
701 localizationsState: this,
702 typeToResources: _typeToResources,
703 child: Directionality(textDirection: _textDirection, child: widget.child!),
704 ),
705 );
706 }
707}
708

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com