flutter中常用的国际化是Intl,通常会生成这样的目录结构
然后在代码中的使用方式为
MaterialApp(
localizationsDelegates: [S.delegate],
),
S.of(context).test;
看到of想必大家能联想起InheritedWidget,其实国际化本质就是依托于InheritedWidget
下面我们从后往前推,先看下S.of(context)
static S of(BuildContext context) {
final instance = S.maybeOf(context);
assert(instance != null,
'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?');
return instance!;
}
static S? maybeOf(BuildContext context) {
return Localizations.of<S>(context, S);
}
这里出现了Localizations
static T? of<T>(BuildContext context, Type type) {
assert(context != null);
assert(type != null);
final _LocalizationsScope? scope = context.dependOnInheritedWidgetOfExactType<_LocalizationsScope>();
return scope?.localizationsState.resourcesFor<T?>(type);
}
然后是_LocalizationsScope,他是我们要找的InheritedWidget
所以,可想而知,我们的国家化的内容用该是通过这里获取
然后我们看看_LocalizationsScope是在哪里创建的
image.png
最终WidgetsApp就是在MaterialApp中创建的
通过上边的源代码发现,我们最初传入的[S.delegate]给到了Localizations的delegates属性,而Localizations的state,是赋值给了_LocalizationsScope的localizationsState属性
所以此时思路应该清晰,Localizations负责管理所有的国际化,而_LocalizationsScope是负责国际化上下文的管理
这个时候我们返回头再看Localizations.of<S>(context, S)干了什么
继续寻找_typeToResources的来源
这里可以看到Localizations的delegates最终转化成了_LocalizationsState的_typeToResources,而_typeToResources是以国际化的类型与国际化的实例作为键值对的形式存储了起来
到此项目中使用国际化的详细流程就结束了
这里我们可以优化的一点是:如果是如果我们不想在使用的时候依托于上下文,来使用国际化,我们可以寻找一个恰当的时机来保存一个_LocalizationsScope创建之后的一个BuildContext,因为这个BuildContext足以通过上下文获取到国际化的内容,这就有很多种方式了,这列举一种:onGenerateTitle中的context
因为通过上下文
这里的title最终会到_LocalizationsScope下游,所以我们可以保存onGenerateTitle中的BuildContext
MaterialApp(
onGenerateTitle: (BuildContext context) {
globalContext = context;
return "title";
},
localizationsDelegates: [S.delegate],
)
T getLanguage<T>() {
final instance = Localizations.of<T>(globalContext, T);
assert(instance != null, '没有找到${T}');
return instance!;
}
在使用的时候,直接提供Type就可以了,实现了国际化与BuildContext树的解耦
getLanguage<S>().test;
👀关注公众号:Android老皮!!!欢迎大家来找我探讨交流👀