- 通过命令行创建项目
flutter create YYFood
; - 用Android Studio 打开新建项目YYFood;
Android的项目配置
- Android的
AppID设置
,如下所示:
-
Android的
应用名称与应用图标设置
,如下所示:
-
Android的
启动图设置
,如下所示:
iOS的项目配置
- 关于iOS的项目配置,不建议直接在Android Studio中设置,而是利用Xcode打开Runner,在Xcode中进行设置,如下所示:
- 设置应用名称,AppID,Icon,启动图,如下所示:
项目的目录结构
国际化
- App国际化开发主要包括:文本国际化(包括文本的顺序),Widget显示的国际化;
Widget的国际化
- Flutter给我们提供的Widget默认情况下就是支持国际化,但是在没有进行特别的设置之前,它们无论在什么环境都是以英文的方式显示的;
- 如果想要添加其他语言,你的应用必须指定额外的 MaterialApp 属性并且添加一个单独的 package,叫做 flutter_localizations;
- 操作步骤入下:
- 第一步:在
pubspec.yaml
文件中添加以下依赖;
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
- 第二步:设置MaterialApp
- 在localizationsDelegates中指定哪些Widget需要进行国际化用于生产本地化值集合的工厂我们这里指定了Material、Widgets、Cupertino都使用国际化
- supportedLocales指定要支持哪些国际化我们这里指定中文和英文(也可以指定国家编码)
MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate, // 指定本地化的字符串和一些其他的值
GlobalCupertinoLocalizations.delegate, // 对应的Cupertino风格
GlobalWidgetsLocalizations.delegate // 指定默认的文本排列方向, 由左到右或由右到左
],
supportedLocales: [
Locale("en"),
Locale("zh")
],
)
- 设置完成后,我们在Android上将语言切换为中文;
- 但是对于iOS,将语言切换为中文,依然显示是英文的Widget,原因在于:这是因为iOS定义了一些应用的元数据,其中包括支持的语言环境,我们必须将其对应的元数据中支持的语言添加进去,元数据的设置在iOS项目中对应的info.plist文件中;
- 修改iOS的info.plist文件配置:
- 选择 Information Property List 项;
- 从 Editor 菜单中选择 Add Item,然后从弹出菜单中选择 Localizations;
- 为array添加一项选择 Add Item,选择Chinese;
- 配置完成后,卸载之前的app,重新安装:
文本国际化
- 第一步:创建本地化类,此类用于定义我们需要进行本地化的字符串等信息;
- 我们需要一个构造器,并且传入一个Locale对象(后续会使用到);
- 定义一个Map,其中存放我们不同语言对应的显示文本;
- 定义它们对应的getter方法,根据语言环境返回不同的结果;
import 'package:flutter/material.dart';
class HYLocalizations {
final Locale locale;
HYLocalizations(this.locale);
static Map<String, Map<String, String>> _localizedValues = {
"en": {
"title": "home",
"greet": "hello~",
"picktime": "Pick a Time"
},
"zh": {
"title": "首页",
"greet": "你好~",
"picktime": "选择一个时间"
}
};
String get title {
return _localizedValues[locale.languageCode]["title"];
}
String get greet {
return _localizedValues[locale.languageCode]["greet"];
}
String get pickTime {
return _localizedValues[locale.languageCode]["picktime"];
}
}
- 第二步:自定义Delegate
- 我们可以像Flutter Widget中的国际化方式一样对它们进行初始化,也就是我们也定义一个对象的Delegate类,并且将其传入localizationsDelegates中;
- Delegate的作用就是当
Locale发生改变时
,调用对应的load方法
,重新加载新的Locale资源
; -
HYLocalizationsDelegate
需要继承自LocalizationsDelegate
,并且有三个方法必须重写:- isSupported:用于当前环境的Locale,是否在我们支持的语言范围
- shouldReload:当Localizations Widget重新build时,是否调用load方法重新加载Locale资源,一般情况下,Locale资源只应该在Locale切换时加载一次,不需要每次Localizations重新build时都加载一遍,所以一般情况下返回false即可;
- load方法:当Locale发生改变时(语言环境),加载对应的HYLocalizations资源,这个方法返回的是一个Future,因为有可能是异步加载的;但是我们这里是直接定义的一个Map,因此可以直接返回一个同步的Future(SynchronousFuture);
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:i18n_demo/i18n/localizations.dart';
class HYLocalizationsDelegate extends LocalizationsDelegate<HYLocalizations> {
@override
bool isSupported(Locale locale) {
return ["en", "zh"].contains(locale.languageCode);
}
@override
bool shouldReload(LocalizationsDelegate<HYLocalizations> old) {
return false;
}
@override
Future<HYLocalizations> load(Locale locale) {
return SynchronousFuture(HYLocalizations(locale));
}
static HYLocalizationsDelegate delegate = HYLocalizationsDelegate();
}
- 第三步:使用本地化类HYLocalization
- 我们可以通过Localizations.of(context, HYLocalizations)获取到HYLocalizations对象
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(Localizations.of(context, HYLocalizations).title),
),
body: Center(
child: Column(
children: <Widget>[
Text(Localizations.of(context, HYLocalizations).greet),
RaisedButton(
child: Text(Localizations.of(context, HYLocalizations).pickTime),
onPressed: () {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2019),
lastDate: DateTime(2022)
).then((pickTime) {
});
},
)
],
),
),
);
}
- 当然,我们可以对Localizations.of(context, HYLocalizations)进行一个优化,给HYLocalizations定义一个of的静态方法;
class HYLocalizations {
static HYLocalizations of(BuildContext context) {
return Localizations.of(context, HYLocalizations);
}
}
- 现在使用方式如下:
appBar: AppBar(
title: Text(HYLocalizations.of(context).title),
)
- 假如我们的数据是异步加载的,比如来自Json文件或者服务器,我们需要
异步加载数据
,我们可以修改HYLocalizations的数据加载:
static Map<String, Map<String, String>> _localizedValues = {};
Future<bool> loadJson() async {
// 1.加载json文件
String jsonString = await rootBundle.loadString("assets/json/i18n.json");
// 2.转成map类型
final Map<String, dynamic> map = json.decode(jsonString);
// 3.注意:这里是将Map<String, dynamic>转成Map<String, Map<String, String>>类型
_localizedValues = map.map((key, value) {
return MapEntry(key, value.cast<String, String>());
});
return true;
}
- 在HYLocalizationsDelegate中使用异步进行加载:
@override
Future<HYLocalizations> load(Locale locale) async {
final localization = HYLocalizations(locale);
await localization.loadJson();
return localization;
}
- 上面我们通过加载本地json文件实现国际化,但是还有另外一个问题,我们在进行国际化的过程中,下面的代码依然需要根据json文件手动编写
String get title {
return _localizedValues[locale.languageCode]["title"];
}
String get greet {
return _localizedValues[locale.languageCode]["greet"];
}
String get pickTime {
return _localizedValues[locale.languageCode]["picktime"];
}
arb文件
- 为了新增字段,不写get代码,我们引入
arb文件
,全称Application Resource Bundle
表示应用资源包,目前已经得到Google的支持; - 其本质就是一个json文件,但是可以根据该文件转成对应的语言环境;
- arb的说明文档:https://github.com/google/app-resource-bundle/wiki/ApplicationResourceBundleSpecification
使用arb -> intl package
- 官方文档推荐可以使用intl package来进行arb和dart文件之间的转换(通过终端指令)
- 需要在在pubspec.yaml中添加其相关的依赖,具体步骤这里不再详细给出,可以参考官方文档:https://flutter.dev/docs/development/accessibility-and-localization/internationalization#appendix-using-the-dart-intl-tools
使用使用IDE插件
- 比较好用的Android Studio的插件
Flutter Intl
,对于Android Studio和VSCode中都是支持的; - 使用步骤如下:
- 安装插件
Flutter Intl
; - 选择工具栏Tools - Flutter Intl - Initialize for the Project,初始化intl;
- 完成上面的操作之后会自动生成如下文件目录:
generated是自动生成的dart代码
和I10n是对应的arb文件目录
- 使用Intl,在localizationsDelegates中配置生成的class,名字是S
- 1.添加对应的delegate
- 2.supportedLocales使用S.delegate.supportedLocales
- 安装插件
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
HYLocalizationsDelegate.delegate,
S.delegate
],
supportedLocales: S.delegate.supportedLocales,
- 因为我们目前还没有对应的本地化字符串,所以需要在intl_en.arb文件中编写:编写后
command + s保存
即可;
{
"title": "home",
"greet": "hello~",
"picktime": "Pick a time"
}
- 在代码中使用,格式为:S.of(context).title
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(S.of(context).title),
),
body: Center(
child: Column(
children: <Widget>[
Text(S.of(context).greet),
RaisedButton(
child: Text(S.of(context).picktime),
onPressed: () {
showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2019),
lastDate: DateTime(2022)
).then((pickTime) {
});
},
)
],
),
),
);
}
- 上面默认是生成支持英文的,现在手动添加支持中文的;
- 默认生成文件,并添加中文字段支持,如下:
- arb文件在实现国际化过程中可传递一些参数;
- 修改对应的arb文件如下:
- {name}:表示传递的参数
{
"title": "home",
"greet": "hello~",
"picktime": "Pick a time",
"sayHello": "hello {name}"
}
- 在使用时,传入对应的参数即可:
Text(S.of(context).sayHello("李银河")),