美文网首页
Flutter入门12 -- 项目实战与国际化

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

作者: YanZi_33 | 来源:发表于2022-01-12 20:50 被阅读0次
    • 通过命令行创建项目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("李银河")),
    

    相关文章

      网友评论

          本文标题:Flutter入门12 -- 项目实战与国际化

          本文链接:https://www.haomeiwen.com/subject/ftaxzltx.html