性久久久久久,性色av浪潮av色欲av,国产日韩精品在线观看,亚洲色成人网一二三区

歡迎您光臨深圳塔燈網(wǎng)絡(luò)科技有限公司!
電話圖標(biāo) 余先生:13699882642

網(wǎng)站百科

為您解碼網(wǎng)站建設(shè)的點(diǎn)點(diǎn)滴滴

Flutter 中的國際化

發(fā)表日期:2018-05 文章編輯:小燈 瀏覽次數(shù):1972

一、前言

從 2015 年接觸 Flutter 到現(xiàn)在也有兩年多時間,在這期間我并沒有正真地去了解這個神奇的框架,只是時不時拉取 master 的最新代碼,編一下 flutter_gallery 看看有什么新特性。但隨著此次 GDD 的召開,F(xiàn)lutter 被 Google 帶到了國內(nèi)開發(fā)者的眼前,相信谷歌是已經(jīng)準(zhǔn)備好讓 Flutter 走上移動開發(fā)歷史的舞臺了。

一款好的移動應(yīng)用該具備什么品質(zhì)?戳中用戶痛點(diǎn)的功能,炫酷的 UI 還是流暢的操作體驗(yàn)?這些都很重要,少了其中任何一點(diǎn)都是得不到用戶青睞的。但今天我要說的雖然不是前面這三個中的哪一個,但也是少了它就不行的“應(yīng)用國際化”。

對于開發(fā)者來說,在 Android 和 iOS 開發(fā)中使用國際化已經(jīng)是老掉牙的套路了,那么在 Flutter 中該如何使用國際化呢?是否也想 Android 一樣只要多配置一個 xml 就能搞定了呢?

二、在 MaterialApp 中添加國際化支持

Flutter 官方鼓勵我們在寫 Flutter 應(yīng)用的時候直接從 MaterialApp 開始,原因是 MaterialApp 為我們集成好了很多 Material Design 所必須的控件,如AnimatedThemen、GridPager 等,另外還通過 MaterialApp 配置了全局路由,方便進(jìn)行頁面的切換。既然如此我們就先從 MaterialApp 開始實(shí)現(xiàn)國際化。國際化涵蓋的不單單只是多國語言,還有文字閱讀方向、時間和日期格式等,但本文僅介紹多國語言的適配,它們幾種還希望讀者自行學(xué)習(xí)和研究。

通常我們新建的 Flutter 應(yīng)用是默認(rèn)不支持多語言的,即使用戶在中文環(huán)境下,顯示的文字仍然是英文,比如下圖所示的日期選擇對話框:

image

那么怎么樣將系統(tǒng)的這些組件國際化呢?首先需要在 pubspec.yaml 中添加如下依賴:

flutter_localizations: sdk: flutter 

接著運(yùn)行:

flutter packages get 

以獲取依賴庫。

當(dāng)上面兩部完成后在 main.dart 中 import 如下:

import 'package:flutter_localizations/flutter_localizations.dart'; 

然后在 MaterialApp 的構(gòu)造方法中給 localizationsDelegatessupportedLocales 兩個可選參數(shù)賦值:

class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Flutter Demo', theme: new ThemeData( primarySwatch: Colors.blue, ), home: new MyHomePage(title: 'Flutter Demo Home Page'), localizationsDelegates: [ //此處 GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, ], supportedLocales: [ //此處 const Locale('zh','CH'), const Locale('en','US'), ], ); } } 

暫時先不用理解這兩個參數(shù)是什么意思,此時如果重新運(yùn)行的話結(jié)果如下圖:

image

細(xì)心的小伙伴可能發(fā)現(xiàn)這個 Dialog 中的文字是變成中文了,但背景中的 titlebar 的文字還是英文,難道老司機(jī)也翻車了?

其實(shí) titlebar 中的這串文字是屬于我們創(chuàng)建的應(yīng)用的,如下:

home: new MyHomePage(title: 'Flutter Demo Home Page') 

Flutter 框架是不知道翻譯這句話。

接下來要做的就是我們自己實(shí)現(xiàn)一個類似 GlobalMaterialLocalizations的東西,用它來實(shí)現(xiàn)多語言。

首先需要準(zhǔn)備在應(yīng)用中用到的字符串,一個剛新建的 Flutter 應(yīng)用用到了四個字符串,如下

  • Flutter Demo
  • Flutter Demo Home Page
  • You have pushed the button this many times:
  • Increment

這里為了簡單我們只增加中文,依次對應(yīng)為:

  • Flutter 示例
  • Flutter 示例主頁面
  • 你一共點(diǎn)擊了這么多次按鈕:
  • 增加

兩種文字準(zhǔn)備后就可以著手寫 Localizations 了,此處的 Localizations 是多國語言資源的匯總。在這里我自定義一個名為 DemoLocalizations 的類,然后將多國資源整合進(jìn)此類:

class DemoLocalizations {final Locale locale;DemoLocalizations(this.locale);static Map<String, Map<String, String>> _localizedValues = { 'en': { 'task title': 'Flutter Demo', 'titlebar title': 'Flutter Demo Home Page', 'click tip': 'You have pushed the button this many times:', 'inc':'Increment' }, 'zh': { 'task title': 'Flutter 示例', 'titlebar title': 'Flutter 示例主頁面', 'click tip': '你一共點(diǎn)擊了這么多次按鈕:', 'inc':'增加' } };get taskTitle{ return _localizedValues[locale.languageCode]['task title']; }get titleBarTitle{ return _localizedValues[locale.languageCode]['titlebar title']; }get clickTop{ return _localizedValues[locale.languageCode]['click tip']; }get inc{ return _localizedValues[locale.languageCode]['inc']; } } 

此時只要能拿到 DemoLocalizations 的對象實(shí)例,就可以調(diào)用它的taskTitle、titleBarTitle、clickTop這三個方法來獲取對應(yīng)的字符串。

定義完 DemoLocalizations 以后,我們就需要想這么一個問題,這個類是誰負(fù)責(zé)初始化呢?答案自然不是我們自己主動去初始化,而是需要一個叫做 LocalizationsDelegate的類來完成,LocalizationsDelegate 是一個抽象類,需要我們?nèi)?shí)現(xiàn)它:

class DemoLocalizationsDelegate extends LocalizationsDelegate<DemoLocalizations>{const DemoLocalizationsDelegate();@override bool isSupported(Locale locale) { return ['en','zh'].contains(locale.languageCode); }@override Future<DemoLocalizations> load(Locale locale) { return new SynchronousFuture<DemoLocalizations>(new DemoLocalizations(locale)); }@override bool shouldReload(LocalizationsDelegate<DemoLocalizations> old) { return false; }static DemoLocalizationsDelegate delegate = const DemoLocalizationsDelegate(); } 

注意 load 方法,DemoLocalizations就是在此方法內(nèi)被初始化的。

接著將 DemoLocalizationsDelegate 添加進(jìn) MaterialApp:

class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: 'Flutter Demo', theme: new ThemeData( primarySwatch: Colors.blue, ), home: new MyHomePage(title: 'Flutter Demo Home Page'), localizationsDelegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, DemoLocalizationsDelegate.delegate, //添加在此處 ], supportedLocales: [ const Locale('zh', 'CH'), const Locale('en', 'US'), ], ); } } 

DemoLocalizationsDelegate 已經(jīng)被添加進(jìn) MaterialApp,那我們該如何使用 DemoLocalizations 呢?這里就要介紹另一個 Weidget 的子類 Localizations,注意此處的 Localizations 它是一個貨真價實(shí) Widget。DemoLocalizationsDelegate 這個類的對象雖然被傳入了 MaterialApp,但由于 MaterialApp 會在內(nèi)部嵌套 Localizations 這個 Widget,而 LocalizationsDelegate 正是其構(gòu)造方法必須的參數(shù):

Localizations({ Key key, @required this.locale, @required this.delegates,//此處 this.child, }) : assert(locale != null),assert(delegates != null),assert(delegates.any((LocalizationsDelegate<dynamic> delegate)=> delegate is LocalizationsDelegate<WidgetsLocalizations>)),super(key: key); 

而 DemoLocalizations 的實(shí)例也是在 Localizations 中通過 DemoLocalizationsDelegate 實(shí)例化的。所以在應(yīng)用中要使用 DemoLocalizations 的實(shí)例自然是需要通過 Localizations 這個 Widget 來獲取的,代碼如下:

Localizations.of(context, DemoLocalizations); 

of這個靜態(tài)方法就會返回 DemoLocalizations 的實(shí)例,現(xiàn)在先別管其內(nèi)部是如何實(shí)現(xiàn)的。我們將這行代碼放入 DemoLocalizations 中以方便使用:

class DemoLocalizations {final Locale locale;DemoLocalizations(this.locale);static Map<String, Map<String, String>> _localizedValues = { 'en': { 'task title': 'Flutter Demo', 'titlebar title': 'Flutter Demo Home Page', 'click tip': 'You have pushed the button this many times:', 'inc':'Increment' }, 'zh': { 'task title': 'Flutter 示例', 'titlebar title': 'Flutter 示例主頁面', 'click tip': '你一共點(diǎn)擊了這么多次按鈕:', 'inc':'增加' } };get taskTitle{ return _localizedValues[locale.languageCode]['task title']; }get titleBarTitle{ return _localizedValues[locale.languageCode]['titlebar title']; }get clickTop{ return _localizedValues[locale.languageCode]['click tip']; }get inc{ return _localizedValues[locale.languageCode]['inc']; }//此處 static DemoLocalizations of(BuildContext context){ return Localizations.of(context, DemoLocalizations); } } 

接下來就是真正使用 DemoLocalizations 的時候了,在代碼中將原來的字符串替換如下:

import 'dart:async';import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter/foundation.dart' show SynchronousFuture;void main() { runApp(new MyApp()); }class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: DemoLocalizations.of(context).taskTitle, // 此處1 theme: new ThemeData( primarySwatch: Colors.blue, ), home: new MyHomePage(title: DemoLocalizations.of(context).titleBarTitle), // 此處2 localizationsDelegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, DemoLocalizationsDelegate.delegate, ], supportedLocales: [ const Locale('zh', 'CH'), const Locale('en', 'US'), ], ); } }class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key);final String title;@override _MyHomePageState createState() => new _MyHomePageState(); }class _MyHomePageState extends State<MyHomePage> { int _counter = 0;void _incrementCounter() { showDatePicker(context: context, initialDate: new DateTime.now(), firstDate: new DateTime.now().subtract(new Duration(days: 30)), lastDate: new DateTime.now().add(new Duration(days: 30))).then((v) {}); }@override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text(widget.title), ), body: new Center( child: new Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ new Text( DemoLocalizations.of(context).clickTop,// 此處3 ), new Text( '$_counter', style: Theme .of(context) .textTheme .display1, ), ], ), ), floatingActionButton: new FloatingActionButton( onPressed: _incrementCounter, tooltip: DemoLocalizations.of(context).inc, // 此處4 child: new Icon(Icons.add), ), ); } } 

運(yùn)行??!

image

???

當(dāng)遇到這種突如其來的問題的時候一定要淡定,喝口水,眺望一會遠(yuǎn)方。。。

接著仔細(xì)看報錯信息:The getter 'taskTitle' was called on null.說的很明確,在 1 處出現(xiàn)了空指針,我們沒有像預(yù)想的一樣拿到 DemoLocalizations 對象。那問題一定出在 Localizations.of 方法內(nèi)部,跟進(jìn)去看看:

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); } 

關(guān)鍵在 context.inheritFromWidgetOfExactType處,繼續(xù)進(jìn)去:

InheritedWidget inheritFromWidgetOfExactType(Type targetType); 

很簡單,這是一個抽象 BuildContext 的抽象方法。此時如果再要繼續(xù)追蹤實(shí)現(xiàn)類就比較困難了,通過這個方法的注釋可以知道,它是通過 targetType 來獲取 context 最近父節(jié)點(diǎn)的對象,前提條件是 targetType 對應(yīng)的類必須是 InheriteWidget 的子類。通過查看 _LocalizationsScope發(fā)現(xiàn)其正是繼承自 InheriteWidget。那就是說沒有從 context 的父節(jié)點(diǎn)中找到 _LocalizationsScope。此時我們再看一下調(diào)用 taskTitle 的地方:

class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( title: DemoLocalizations.of(context).taskTitle, // 此處 theme: new ThemeData( primarySwatch: Colors.blue, ), home: new MyHomePage(title: DemoLocalizations.of(context).titleBarTitle), localizationsDelegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, DemoLocalizationsDelegate.delegate, ], supportedLocales: [ const Locale('zh', 'CH'), const Locale('en', 'US'), ], ); } } 

仔細(xì)看 taskTitle 處的 context 是從最外層的 build 方法中傳入的,而在之前說過 Localizations 這個組件是在 MaterialApp 中被嵌套的,也就是說能找到 DemoLocalizations 的 context 至少需要是 MaterialApp 內(nèi)部的,而此時的 context 是無法找到 DemoLocalizations 對象的。但這樣進(jìn)入死胡同了,實(shí)現(xiàn)多語言的 DemoLocalizations 需要在 MaterialApp 內(nèi)部才能被找到,而這里的 title 用到的 context 是在 MaterialApp 外部的。

難道多語言在 title 上沒法實(shí)現(xiàn)?

喝口水,眺望下遠(yuǎn)方。

既然如此我們不如看下這個 title 的說明:

/// A one-line description used by the device to identify the app for the user. /// /// On Android the titles appear above the task manager's app snapshots which are /// displayed when the user presses the "recent apps" button. Similarly, on /// iOS the titles appear in the App Switcher when the user double presses the /// home button. /// /// To provide a localized title instead, use [onGenerateTitle]. /// /// This value is passed unmodified to [WidgetsApp.title]. final String title; 

請注意這句:To provide a localized title instead, use [onGenerateTitle].

沒想到啊,如果要對 title 進(jìn)行多語言處理還需要 onGenerateTitle這個屬性。那就簡單了,更改如下:

class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( onGenerateTitle: (context){// 此處 return DemoLocalizations.of(context).taskTitle; }, theme: new ThemeData( primarySwatch: Colors.blue, ), home: new MyHomePage(title: DemoLocalizations.of(context).titleBarTitle), localizationsDelegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, DemoLocalizationsDelegate.delegate, ], supportedLocales: [ const Locale('zh', 'CH'), const Locale('en', 'US'), ], ); } } 

此時運(yùn)行會發(fā)現(xiàn) taskTitle 處已經(jīng)沒問題了,但 titleBarTitle 這邊還是報錯,原因一樣它的 context 使用的是 MaterialApp 外部的 context。但這里的 title 是可以被移動到 MyHomePage 內(nèi)部初始的,所以很好修改,將 MyHomePage 構(gòu)造方法中的 title 參數(shù)移除,直接在 AppBar 內(nèi)部賦值:

class MyHomePage extends StatefulWidget { MyHomePage({Key key}) : super(key: key);@override _MyHomePageState createState() => new _MyHomePageState(); }class _MyHomePageState extends State<MyHomePage> { int _counter = 0;void _incrementCounter() { showDatePicker(context: context, initialDate: new DateTime.now(), firstDate: new DateTime.now().subtract(new Duration(days: 30)), lastDate: new DateTime.now().add(new Duration(days: 30))).then((v) {}); }@override Widget build(BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text(DemoLocalizations.of(context).titleBarTitle),// 此處 ), body: new Center( child: new Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ new Text( DemoLocalizations.of(context).clickTop, ), new Text( '$_counter', style: Theme .of(context) .textTheme .display1, ), ], ), ), floatingActionButton: new FloatingActionButton( onPressed: _incrementCounter, tooltip: DemoLocalizations.of(context).inc, child: new Icon(Icons.add), ), ); } } 

再運(yùn)行:

image image

完美。

三、國際化的初始化過程

上一節(jié)中簡單介紹了如何在 MaterialApp 實(shí)現(xiàn)國際化,各位可能也注意到了最終語言資源的選擇還是留給了 DemoLocalizations,而對語言資源本身是以什么形式存在沒有特別規(guī)定。在上文中我將兩國的語言放到了一個 Map 中,自然也可以將其放在服務(wù)器上,在程序啟動后進(jìn)行拉取,這些都是后話了,在這一節(jié)中我簡單剖析下源碼,看看 DemoLocalizatins 是如何在程序運(yùn)行后被初始化的。

上面已經(jīng)說過官方鼓勵我們使用 MaterialApp 作為程序入口,我們就從 MaterialApp 出發(fā),首先看 MaterialApp 的構(gòu)造方法:

MaterialApp({ // can't be const because the asserts use methods on Map :-( Key key, this.title: '', this.onGenerateTitle, this.color, this.theme, this.home, this.routes: const <String, WidgetBuilder>{}, this.initialRoute, this.onGenerateRoute, this.onUnknownRoute, this.locale, this.localizationsDelegates, this.localeResolutionCallback, this.supportedLocales: const <Locale>[const Locale('en', 'US')], this.navigatorObservers: const <NavigatorObserver>[], this.debugShowMaterialGrid: false, this.showPerformanceOverlay: false, this.checkerboardRasterCacheImages: false, this.checkerboardOffscreenLayers: false, this.showSemanticsDebugger: false, this.debugShowCheckedModeBanner: true }) 

上面的 localizationsDelegates是多語言的關(guān)鍵點(diǎn),由于 MaterialApp 是一個 StatefulWidget,所以直接看其對應(yīng)的 State 類 _MaterialAppState中的 build 方法,代碼有點(diǎn)長:

Widget build(BuildContext context) { final ThemeData theme = widget.theme ?? new ThemeData.fallback(); Widget result = new AnimatedTheme(// 1 data: theme, isMaterialAppTheme: true, child: new WidgetsApp(//2 key: new GlobalObjectKey(this), title: widget.title, onGenerateTitle: widget.onGenerateTitle, textStyle: _errorTextStyle, // blue is the primary color of the default theme color: widget.color ?? theme?.primaryColor ?? Colors.blue, navigatorObservers: new List<NavigatorObserver>.from(widget.navigatorObservers) ..add(_heroController), initialRoute: widget.initialRoute, onGenerateRoute: _onGenerateRoute, onUnknownRoute: _onUnknownRoute, locale: widget.locale, localizationsDelegates: _localizationsDelegates,//3 localeResolutionCallback: widget.localeResolutionCallback, supportedLocales: widget.supportedLocales, showPerformanceOverlay: widget.showPerformanceOverlay, checkerboardRasterCacheImages: widget.checkerboardRasterCacheImages, checkerboardOffscreenLayers: widget.checkerboardOffscreenLayers, showSemanticsDebugger: widget.showSemanticsDebugger, debugShowCheckedModeBanner: widget.debugShowCheckedModeBanner, inspectorSelectButtonBuilder: (BuildContext context, VoidCallback onPressed) { return new FloatingActionButton( child: const Icon(Icons.search), onPressed: onPressed, mini: true, ); }, ) );assert(() { if (widget.debugShowMaterialGrid) {//此處如果有配置,則會顯示網(wǎng)格 result = new GridPaper( color: const Color(0xE0F9BBE0), interval: 8.0, divisions: 2, subdivisions: 1, child: result, ); } return true; }());return new ScrollConfiguration(// 4 behavior: new _MaterialScrollBehavior(), child: result, ); } 

首先在 3 處可以看到 _localizationsDelegates 被賦值給了 WidgetsApp 的 localizationsDelegates 參數(shù)。在看 1、2、4 處分別又在原有的 Widget 上做了包裹,此時的 widget 樹層次如下圖:

[圖片上傳失敗...(image-74770b-1525245587456)]

接著進(jìn)入 WidgetApp ,它也是個 StatefulWidget,直接看它的 State 類 _WidgetsAppState的 build 方法:

 Widget build(BuildContext context) { Widget result = new Navigator(// 1 key: _navigator, initialRoute: widget.initialRoute ?? ui.window.defaultRouteName, onGenerateRoute: widget.onGenerateRoute, onUnknownRoute: widget.onUnknownRoute, observers: widget.navigatorObservers, );if (widget.textStyle != null) { result = new DefaultTextStyle(//2 style: widget.textStyle, child: result, ); }... //此處省略調(diào)試相關(guān)代碼return new MediaQuery(//3 data: new MediaQueryData.fromWindow(ui.window), child: new Localizations( //4 locale: widget.locale ?? _locale, delegates: _localizationsDelegates.toList(), // This Builder exists to provide a context below the Localizations widget. // The onGenerateCallback() can refer to Localizations via its context // parameter. child: new Builder( //5 builder: (BuildContext context) { String title = widget.title; if (widget.onGenerateTitle != null) { title = widget.onGenerateTitle(context); assert(title != null, 'onGenerateTitle must return a non-null String'); } return new Title(//6 title: title, color: widget.color, child: result, ); }, ), ), ); } 

在 4 處終于見到了我們熟悉的身影 Localizatins。_localizationsDelegates 也是被傳遞進(jìn)了 Localizations。此時的 widget 樹層次如下:

[圖片上傳失敗...(image-901681-1525245587456)]

層次如此之多,但我們關(guān)心只是其中的 Localizations,所以拋開其他不看,進(jìn)入 Localizations 看看。

不出意外 Localizations 也是一個 StatefulWidget,此時我們不需要關(guān)心它的 build 方法,而是應(yīng)該關(guān)注其內(nèi)部的 initState 方法,如果有數(shù)據(jù)需要初始化,不出意外就是在這里進(jìn)行。

initState 方法很短:

@override void initState() { super.initState(); load(widget.locale); } 

繼續(xù)進(jìn)入 load 方法:

void load(Locale locale) { final Iterable<LocalizationsDelegate<dynamic>> delegates = widget.delegates; // 1 if (delegates == null || delegates.isEmpty) { _locale = locale; return; }Map<Type, dynamic> typeToResources; final Future<Map<Type, dynamic>> typeToResourcesFuture = _loadAll(locale, delegates) //2 .then((Map<Type, dynamic> value) { return typeToResources = value; });...} 

1 處的 delegates 即一開始從 MaterialApp 傳入的 delegate 數(shù)組,這里轉(zhuǎn)成立可迭代對象。接著看 2 處的 _loadAll 方法返回的 typeToResourcesFuture ,其中的值類型為 Map<Type, dynamic>,這里可以推敲出來里邊的 Type 對應(yīng)的就是不同的 Localizations,而 dynamic 則是其實(shí)例。帶著這樣的想法看 _loadAll 方法:

Future<Map<Type, dynamic>> _loadAll(Locale locale, Iterable<LocalizationsDelegate<dynamic>> allDelegates) { final Map<Type, dynamic> output = <Type, dynamic>{}; List<_Pending> pendingList;// Only load the first delegate for each delegate type that supports // locale.languageCode. final Set<Type> types = new Set<Type>(); final List<LocalizationsDelegate<dynamic>> delegates = <LocalizationsDelegate<dynamic>>[]; for (LocalizationsDelegate<dynamic> delegate in allDelegates) { if (!types.contains(delegate.type) && delegate.isSupported(locale)) { types.add(delegate.type); delegates.add(delegate); } }for (LocalizationsDelegate<dynamic> delegate in delegates) { final Future<dynamic> inputValue = delegate.load(locale);// 1 dynamic completedValue; final Future<dynamic> futureValue = inputValue.then<dynamic>((dynamic value) { return completedValue = value; // 2 }); if (completedValue != null) { // inputValue was a SynchronousFuture final Type type = delegate.type; assert(!output.containsKey(type)); output[type] = completedValue; } else { pendingList ??= <_Pending>[]; pendingList.add(new _Pending(delegate, futureValue)); } }// All of the delegate.load() values were synchronous futures, we're done. if (pendingList == null) return new SynchronousFuture<Map<Type, dynamic>>(output);// Some of delegate.load() values were asynchronous futures. Wait for them. return Future.wait<dynamic>(pendingList.map((_Pending p) => p.futureValue)) .then<Map<Type, dynamic>>((List<dynamic> values) { assert(values.length == pendingList.length); for (int i = 0; i < values.length; i += 1) { final Type type = pendingList[i].delegate.type; assert(!output.containsKey(type)); output[type] = values[i]; } return output; }); } 

看 1 處,調(diào)用到了 deletegate 的 load 方法,返回一個 Future ,這里為什么不直接返回DemoLocalizations 的實(shí)例而要返回 Future,這個在前面也提到了如果你的資源是放在服務(wù)器上的,那么這就是一個耗時操作,所以在此處用了 Future。

@override Future<DemoLocalizations> load(Locale locale) { return new SynchronousFuture<DemoLocalizations>(new DemoLocalizations(locale)); } 

由于這里返回的是 SynchronousFuture ,所以在 2 處的代碼會被順序執(zhí)行,此時 completedValue 就是 DemoLocalizations 的實(shí)例對象了。然后 completedValue 被放入了 output 接著就返回出去了,最后賦值給了 _LocalizationsState 的 _typeToResources 變量。

到目前為止整個多語言的加載就完成了,剩下的就是等著被使用。下面看一下使用的方式:

DemoLocalizations.of(context).taskTitle 

簡單粗暴,根本看不出來是怎么拿到 DemoLocalizations 對象的。不多說,看代碼:

return Localizations.of(context, DemoLocalizations); 

內(nèi)部調(diào)用的是 Localizations 的 of 靜態(tài)方法,接著看:

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); } 

前面已經(jīng)講解過 context.inheritFromWidgetOfExactType 的作用,這里的 scope 就是最靠近 context 節(jié)點(diǎn)的 _LocalizationsScope 類型的節(jié)點(diǎn)。但我們看了上面的 widget 樹的層次圖,并沒有看到 _LocalizationsScope 這個 widget,它是在什么時候被添加進(jìn)去的呢?

回到 _LocalizationsState 的 build 方法:

@override Widget build(BuildContext context) { if (_locale == null) return new Container(); return new _LocalizationsScope( key: _localizedResourcesScopeKey, locale: _locale, localizationsState: this, typeToResources: _typeToResources, child: new Directionality( textDirection: _textDirection, child: widget.child, ), ); } 

真想(●—●)。在 Localizations 的內(nèi)部,它將它原本的子節(jié)點(diǎn)外又嵌套了 Directionality、_LocalizationsScope、Container 這三層。其中 _LocalizationsScope 就是我們想找的。

接著看:

return scope?.localizationsState?.resourcesFor<T>(type); 

此處調(diào)用了 _LocalizationsState 的 resourcesFor 方法:

T resourcesFor<T>(Type type) { assert(type != null); final T resources = _typeToResources[type]; return resources; } 

到這差不多就結(jié)束了,這里根據(jù) type 從 _typeToResources 中取出了 DemoLocalizations 的實(shí)例。
最后再把完整的 widget 樹的層次展示一下:

[圖片上傳失敗...(image-d220cd-1525245587456)]

四、簡單的 App 內(nèi)語言切換

下面我見到介紹一下如何在不切換手機(jī)系統(tǒng)的語言的情況下來切換 Flutter 應(yīng)用內(nèi)的語言。主要用到的是 Localizations 的 override 方法。具體不多介紹,看下面我自定義的 StatefulWidget 類 FreeLocalizations 和它的 State 類 _FreeLocalizations:

class FreeLocalizations extends StatefulWidget{final Widget child;FreeLocalizations({Key key,this.child}):super(key:key);@override State<FreeLocalizations> createState() { return new _FreeLocalizations(); } }class _FreeLocalizations extends State<FreeLocalizations>{Locale _locale = const Locale('zh','CH');changeLocale(Locale locale){ setState((){ _locale = locale; }); }@override Widget build(BuildContext context) { return new Localizations.override( context: context, locale: _locale, child: widget.child, ); } } 

上面代碼的意思比較清晰,就是在調(diào)用 changeLocale 方法的時候修改其內(nèi)部 widget 的語言。
下面來如何使用:

void main() { runApp(new MyApp()); }GlobalKey<_FreeLocalizations> freeLocalizationStateKey = new GlobalKey<_FreeLocalizations>(); // 1 class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( onGenerateTitle: (context){ return DemoLocalizations.of(context).taskTitle; }, theme: new ThemeData( primarySwatch: Colors.blue, ), home: new Builder(builder: (context){ return new FreeLocalizations( key: freeLocalizationStateKey, child: new MyHomePage(), ); }), localizationsDelegates: [ GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, DemoLocalizationsDelegate.delegate, ], supportedLocales: [ const Locale('zh', 'CH'), const Locale('en', 'US'), ], ); } }

注意想要在 FreeLocalizations 外部去調(diào)用其方法需要使用到 GlobalKey 的幫助,用法見 1 處。讓后我們將 MyHomePage 放入 FreeLocalizations 內(nèi)部。

接著在點(diǎn)擊按鈕的時候調(diào)用如下方法:

void changeLocale(){ if(flag){ freeLocalizationStateKey.currentState.changeLocale(const Locale('zh',"CH")); }else{ freeLocalizationStateKey.currentState.changeLocale(const Locale('en',"US")); } flag = !flag; } 

效果如下:

image

這一小節(jié)我講的比較簡單,但如果你看明白了二、三兩節(jié),那弄明白這里多語言是怎么切換的應(yīng)該是比較容易的。

五、總結(jié)

思維導(dǎo)圖地址:https://my.mindnode.com/7u6RudyGs5bqzX1WrxY5XtZZqUDBzqvL2NioVbrr
文章中出現(xiàn)的代碼的地址:https://github.com/flutter-dev/internationalizing


本頁內(nèi)容由塔燈網(wǎng)絡(luò)科技有限公司通過網(wǎng)絡(luò)收集編輯所得,所有資料僅供用戶學(xué)習(xí)參考,本站不擁有所有權(quán),如您認(rèn)為本網(wǎng)頁中由涉嫌抄襲的內(nèi)容,請及時與我們聯(lián)系,并提供相關(guān)證據(jù),工作人員會在5工作日內(nèi)聯(lián)系您,一經(jīng)查實(shí),本站立刻刪除侵權(quán)內(nèi)容。本文鏈接:http://caipiao93.cn/18070.html
相關(guān)APP開發(fā)
 八年  行業(yè)經(jīng)驗(yàn)

多一份參考,總有益處

聯(lián)系深圳網(wǎng)站公司塔燈網(wǎng)絡(luò),免費(fèi)獲得網(wǎng)站建設(shè)方案及報價

咨詢相關(guān)問題或預(yù)約面談,可以通過以下方式與我們聯(lián)系

業(yè)務(wù)熱線:余經(jīng)理:13699882642

Copyright ? 2013-2018 Tadeng NetWork Technology Co., LTD. All Rights Reserved.