Flutter入门12 -- 项目实战与国际化

  • 通过命令行创建项目flutter create YYFood
  • 用Android Studio 打开新建项目YYFood;

Android的项目配置

  • Android的AppID设置,如下所示:
image.png
  • Android的应用名称与应用图标设置,如下所示:

    image.png

  • Android的启动图设置,如下所示:

    image.png

iOS的项目配置

  • 关于iOS的项目配置,不建议直接在Android Studio中设置,而是利用Xcode打开Runner,在Xcode中进行设置,如下所示:
image.png
  • 设置应用名称,AppID,Icon,启动图,如下所示:
Snip20211102_9.png
Snip20211102_10.png
Snip20211102_11.png
image.png

项目的目录结构

国际化

  • 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,重新安装:
image.png
文本国际化
  • 第一步:创建本地化类,此类用于定义我们需要进行本地化的字符串等信息;
    • 我们需要一个构造器,并且传入一个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文件

使用arb -> intl package

使用使用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) {
                });
              },
            )
          ],
        ),
      ),
    );
  }
  • 上面默认是生成支持英文的,现在手动添加支持中文的;
Snip20211105_23.png
Snip20211105_24.png
  • 默认生成文件,并添加中文字段支持,如下:
Snip20211105_25.png
Snip20211105_26.png
  • arb文件在实现国际化过程中可传递一些参数;
  • 修改对应的arb文件如下:
  • {name}:表示传递的参数
{
  "title": "home",
  "greet": "hello~",
  "picktime": "Pick a time",
  "sayHello": "hello {name}"
}
  • 在使用时,传入对应的参数即可:
Text(S.of(context).sayHello("李银河")),
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 概述 国际化的认识 国际化的适配 国际化的工具 一、国际化的认识 开发一个App,如果我们的App需要面向不同的语...
    IIronMan阅读 698评论 0 2
  • 一. 国际化的认识 开发一个App,如果我们的App需要面向不同的语种(比如中文、英文、繁体等),那么我们需要对齐...
    AlanGe阅读 407评论 0 0
  • 一. 国际化的认识 开发一个App,如果我们的App需要面向不同的语种(比如中文、英文、繁体等),那么我们需要对齐...
    5e4c664cb3ba阅读 1,394评论 0 3
  • 如果App的用户使用的是不同语言,那进行国际化是必要的。国际化主要包括文案的国际化(不同的语言展示不同的文案)和布...
    chonglingliu阅读 2,116评论 5 6
  • Flutter 国际化 详情链接[https://book.flutterchina.club/chapter13...
    土豆骑士阅读 1,752评论 1 5